Prv8 Shell
Server : Apache
System : Linux ecngx264.inmotionhosting.com 4.18.0-553.77.1.lve.el8.x86_64 #1 SMP Wed Oct 8 14:21:00 UTC 2025 x86_64
User : lonias5 ( 3576)
PHP Version : 7.3.33
Disable Function : NONE
Directory :  /proc/self/root/proc/thread-self/root/opt/sharedrads/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //proc/self/root/proc/thread-self/root/opt/sharedrads/limit_bots
#!/usr/lib/rads/venv/bin/python3
import argparse
import os
import platform
import pwd
import re
import sys
from pathlib import Path
import yaml
from pp_api import PowerPanel
from rads import CpuserError, get_owner, OUR_RESELLERS, UserData

pp = PowerPanel()


class BotBlocker:
    def __init__(self, user: str, **kwargs):
        # Generate list of bots to exclude from block list
        local_bot_excludes = Path(f'/home/{user}/.imh/limit_bots.yaml')
        try:
            with open(local_bot_excludes, encoding='utf-8') as bots_file:
                bot_excludes = yaml.safe_load(bots_file)
        except Exception:
            bot_excludes = []

        # Generate list of bots to block in .htaccess
        if kwargs.get('bots'):
            self.bots = [
                bot for bot in kwargs.get('bots') if bot not in bot_excludes
            ]
        else:
            self.bots = [
                bot
                for bot in [
                    'AhrefsBot',
                    'Amazonbot',
                    'Barkrowler',
                    'Bytespider',
                    'ClaudeBot',
                    'DotBot',
                    'Googlebot',
                    'GPTBot',
                    'MJ12bot',
                    'PetalBot',
                    'SemrushBot',
                    'bingbot',
                    'facebookexternalhit',
                    'meta-externalagent',
                ]
                if bot not in bot_excludes
            ]

        # cPanel user
        self.user = user

        # .htaccess rule to Block bots
        self.htaccess_rule = (
            "RewriteEngine On\n"
            f"RewriteCond %{{HTTP_USER_AGENT}} ({'|'.join(self.bots)}) [NC]\n"
            "RewriteRule .* - [F,L]"
        )

    def __repr__(self) -> str:
        return f"BotBlocker(user={self.user!r}, bots={self.bots!r})"

    def write_htaccess(self, docroot):
        """block bots by adding to .htaccess"""
        htaccess_path = Path(docroot) / ".htaccess"
        block_start = "# BEGIN InMotion Hosting Block Bots"
        block_end = "# END InMotion Hosting Block Bots"

        if htaccess_path.exists():
            # Read existing content
            existing_content = htaccess_path.read_text()

            # Regex pattern to match the block between BEGIN and END tags
            pattern = re.compile(
                rf'({block_start})(.*?){block_end}\n', re.DOTALL
            )

            # Replace the block content if it exists
            if pattern.search(existing_content):
                updated_content = pattern.sub(
                    rf'{block_start}\n{self.htaccess_rule}\n{block_end}\n',
                    existing_content,
                )
                htaccess_path.write_text(updated_content)
                print(f"Replaced existing block in {htaccess_path}")
            else:
                # Append new block if it doesn't exist
                htaccess_path.write_text(
                    f'{block_start}\n{self.htaccess_rule}\n{block_end}\n'
                    + existing_content
                )
                print(f"Added new block to {htaccess_path}")
        else:
            # Create .htaccess file and write the block snippet
            htaccess_path.write_text(
                f'{block_start}\n{self.htaccess_rule}\n{block_end}\n'
            )

            # Set ownership to user:user
            uid = pwd.getpwnam(self.user).pw_uid
            gid = pwd.getpwnam(self.user).pw_gid
            os.chown(htaccess_path, uid=uid, gid=gid)

            print(f"{htaccess_path} file created with block bots snippet")

    def write_robots_txt(self, docroot):
        """ratelimit bots by creating robots.txt"""
        robots_txt_path = Path(docroot) / "robots.txt"

        # Check if robots.txt already exists
        if robots_txt_path.exists():
            print(f"{robots_txt_path} already exists. No changes made.")
            return

        # Write the robots.txt file if it doesn't exist
        robots_txt_path.write_text("User-agent: *\nCrawl-delay: 30\n")

        # Set ownership to user:user
        uid = pwd.getpwnam(self.user).pw_uid
        gid = pwd.getpwnam(self.user).pw_gid
        os.chown(robots_txt_path, uid=uid, gid=gid)

        print(f"Rate limit added to {robots_txt_path}")


def get_calling_username():
    try:
        blame = f'{os.getlogin()}:{pwd.getpwuid(os.geteuid()).pw_name}'
    except OSError:
        blame = pwd.getpwuid(os.geteuid()).pw_name
    return blame


def send_pp_email(user: str, bots: list):
    """Send suspension email"""
    # IMH - High Resource Usage from Bots id 1068
    # WHH - High Resource Usage from Bots id 1069
    # IMH Reseller - High Resource Usage from Bots for Sub-Account id 1070

    # variable2 == user
    if 'hub' in platform.node():
        template_id = '1069'
        send_to = user
    else:
        # If the customer is the child of a reseller, use the
        # reseller template instead
        owner = get_owner(user)
        if owner not in OUR_RESELLERS:
            template_id = '1070'
            send_to = owner
        else:
            template_id = '1068'
            send_to = user

    template_info = pp.call("notification.fetch-template", template=template_id)
    if template_info.status == 0:
        variables = {}
        for variable in template_info.data['variables']:
            if variable['description'] == "Child User":
                variables[variable['name']] = user
            elif variable['description'] == "USER_AGENTS":
                variables[variable['name']] = ', '.join(bots)

        response = pp.call(
            "notification.send",
            template=template_id,
            cpanelUser=send_to,
            **variables,
        )
        if response.status == 0:
            print("Sent email, review at %s" % response.data['reviewUrl'])
            logged_in_user = get_calling_username().split(':')[0]
            if logged_in_user == "root":
                reporter = "auto"
            else:
                reporter = logged_in_user
            pp(
                'hosting.insert-note',
                user=user,
                admin_user=reporter,
                flagged=False,
                type='SYS - RA',
                # Prepend user to the note because the hosting.insert-note
                # endpoint doesn't seem to honor the 'admin_user' parameter
                # This issue is tracked in Devel #4775
                # https://trac.imhtech.net/Development/ticket/4775
                note=f'{reporter}: High Resource Usage from Bots',
            )

            return
    print(
        "Could not send Power Panel email or note acct, "
        "please do this manually!"
    )


def parse_args():
    """Parse args"""
    parser = argparse.ArgumentParser(
        description="Limit bots with robots.txt and block them in .htaccess"
    )
    parser.add_argument("user", type=str, help="cPanel username")
    parser.add_argument(
        "--domains", nargs='+', help="List of domains to limit/block"
    )
    parser.add_argument(
        "--all-domains",
        action="store_true",
        help="Apply changes to all domains for the user",
    )
    parser.add_argument(
        "--bots", nargs='+', help="List of bots to block (override defaults)"
    )

    return parser.parse_args()


def main():
    # Parse args
    args = parse_args()

    # Exit if invalid arguments were provided
    if not args.all_domains and not args.domains:
        print(
            "Please provide args to --domains or use --all-domains. Examples:"
        )
        print(
            " - limit_bots userna5 --domains domain1.com --bots "
            "facebookexternalhit Amazonbot"
        )
        print(" - limit_bots userna5 --domains domain1.com domain2.com")
        print(" - limit_bots userna5 --all-domains")
        sys.exit(1)

    # Retrieve UserData for user
    try:
        udata = UserData(args.user)
    except CpuserError:
        print("Invalid cPanel User. exiting")
        return

    # Create BotBlocker object
    bot_blocker = BotBlocker(udata.user, bots=args.bots)

    # Create list of docroots to limit
    docroots = []
    if args.all_domains:
        docroots = udata.all_roots
    else:
        # Retrieve docroot for each domain provided in args from UserData
        domains = [udata.primary] + udata.addons + udata.subs + udata.parked
        docroots.extend(
            domain.docroot
            for domain in domains
            if domain.domain in args.domains
        )

    # Apply block/limit to docroots
    for docroot in docroots:
        bot_blocker.write_htaccess(docroot)
        bot_blocker.write_robots_txt(docroot)

    # Send Power Panel e-mail to user
    send_pp_email(udata.user, bot_blocker.bots)


if __name__ == "__main__":
    main()

@StableExploit - 2025