Mini Shell

Direktori : /proc/self/root/proc/self/root/usr/local/bin/
Upload File :
Current File : //proc/self/root/proc/self/root/usr/local/bin/mal-scan

#!/opt/imh-python/bin/python3
"""Wrapper around maldet"""
# author: Kyle Yetter
import sys
import os
import re
import pwd
import subprocess
from optparse import OptionParser

VERSION = '1.4.1'

# Top level constant definition for nice command pamaraters
IO_NICE_LEVEL = '6'
NICE_LEVEL = '10'
MALDET_PATH = '/opt/maldetect/maldet'

###############################################################################
############################## Utility Functions ##############################
###############################################################################

def shell_escape(token):
    """escapes special characters in shell tokens"""
    token = str(token)
    if len(token) == 0:
        return "''"
    token = re.sub(r'([^A-Za-z0-9_=\-\.,:\/@\n])', r'\\\1', token)
    token = re.sub('\n', r'\n', token)
    return token

def shell_join(*tokens):
    """Join a list of strings while shell scaping each"""
    return ' '.join([shell_escape(t) for t in tokens])

def which(program):
    """Return path to executable based on env $PATH or None on error"""
    search_path = os.environ.get('PATH', '').split(os.pathsep)
    for directory in search_path:
        exec_path = os.path.join(directory, program)
        if os.path.isfile(exec_path) and os.access(exec_path, os.X_OK):
            return os.path.abspath(exec_path)
    return None

def scan_yaml(yaml_file, key):
    """Kyleism of yaml.load()"""
    pattern = fr'^\s*({re.escape(key)})\s*:\s*(\S.*)$'
    with open(yaml_file) as handle:
        lines = handle.readlines()
    for line in lines:
        match = re.match(pattern, line.strip())
        if match:
            return match.group(2)
    return None

def find_doc_root(user):
    """Find primary domain docroot"""
    cpanel_userdata_file = os.path.join(
        "/var/cpanel/userdata",
        user.pw_name,
        "main"
    )
    if os.path.isfile(cpanel_userdata_file):
        main_domain = scan_yaml(cpanel_userdata_file, 'main_domain')
        if main_domain:
            domain_config_file = os.path.join(
                "/var/cpanel/userdata",
                user.pw_name,
                main_domain
            )
            if os.path.isfile(domain_config_file):
                doc_root = scan_yaml(domain_config_file, 'documentroot')
                if doc_root and os.path.isdir(doc_root):
                    return doc_root
    public_html_path = os.path.join(user.pw_dir, 'public_html')
    if os.path.isdir(public_html_path):
        return public_html_path

def fail(message):
    """Kyleism of sys.exit(str)"""
    print("\033[31m%s\033[0m" % message, file=sys.stderr)
    option_parser.print_help(sys.stderr)
    sys.exit(1)


#
# These constants hold the full paths to ionice, nice, and sudo -
# used to determine whether each command is available
#
IO_NICE = which('ionice')
NICE = which('nice')
SUDO = which('sudo')


###############################################################################
######################### Command Line Option Parsing #########################
###############################################################################

usage = """
    %prog [--scan-recent] (~user/public_html/path/... | userna5)
    %prog --report SCANID userna5
    %prog --quarantine SCANID userna5
    %prog --clean SCANID userna5
    %prog --log userna5
"""

option_parser = OptionParser(usage=usage)

option_parser.add_option(
    '-q', '--quarantine',
    dest="quarantine",
    metavar="SCANID",
    help="Quarantine any infected files found during the scan"
)

option_parser.add_option(
    '-e', '--report',
    dest="report",
    metavar="SCANID",
    help="View report from the previous scan given by SCANID"
)

option_parser.add_option(
    '-s', '--restore',
    dest="restore",
    metavar="SCANID",
    help="Restore files quarantined from a previous scan given by SCANID"
)

option_parser.add_option(
    "-n", "--clean",
    dest="clean",
    metavar="SCANID",
    help="Try to clean & restore malware hits from report SCANID"
)

option_parser.add_option(
    '-l', '--log',
    dest="log",
    action="store_true",
    help="View maldetect log file events"
)

option_parser.add_option(
    "-r", "--scan-recent",
    dest="recent",
    action="store_true",
    help="Scan files created/modified in the last X days "
    "(default: 7d, wildcard: ?)"
)

option_parser.add_option(
    "-p", "--purge",
    dest="purge",
    action="store_true",
    help="Clear logs, quarantine queue, session and temporary data."
)

option_parser.add_option(
    "-c", "--no-clam",
    dest="clam",
    action="store_true",
    help="Disable the clam engine for scanning"
)

###############################################################################
############################## Main Script Action #############################
###############################################################################

# target_path contains an absolute path to a directory to scan
target_path = os.path.realpath('.')
# target_user is the system passwd entry for the user that
# the scan will execute as
target_user = pwd.getpwuid(os.stat('.').st_uid)

options, args = option_parser.parse_args()


if len(args) > 0:
    arg = args[0]

    if os.path.exists(arg):
        target_path = os.path.realpath(arg)
        target_user = pwd.getpwuid(os.stat(target_path).st_uid)
    else:
        try:
            target_user = pwd.getpwnam(arg)
        except KeyError as e:
            fail(
                "cannot locate a user or path "
                "associated with argument %r"  % (arg,)
            )
    target_path = find_doc_root(target_user)

if target_user.pw_uid == 0:
    fail("mal-scan cannot act on paths owned by root")

if target_user.pw_uid < 500:
    fail("mal-scan cannot act on paths owned by privileged system users")

command_parts = []

if NICE:
    command_parts.extend([NICE, '-n', NICE_LEVEL])

if IO_NICE:
    return_val = os.system("ionice -c 2 -n 4 echo >/dev/null 2>&1")
    if return_val == 0:
        command_parts.extend([IO_NICE, '-c', '2', '-n', IO_NICE_LEVEL])

command_parts.extend([SUDO, '-u', target_user.pw_name])
command_parts.append(MALDET_PATH)
scan_argument = None

if os.path.isdir(target_user.pw_dir):
    os.chdir(target_user.pw_dir)

if options.report:
    command_parts.extend(['--report', options.report])
elif options.quarantine:
    command_parts.extend(['--quarantine', options.quarantine])
elif options.clean:
    command_parts.extend(['--clean', options.clean])
elif options.restore:
    command_parts.extend(['--restore', options.restore])
elif options.log:
    command_parts.extend(['--log'])
elif options.purge:
    command_parts.extend(['--purge'])
elif os.path.isdir(target_path):
    scan_argument = os.path.realpath(target_path)
elif os.path.isfile(target_path):
    scan_argument = os.path.realpath(target_path)

if options.clam:
    command_parts.extend(['--config-option', 'clamav_scan=0'])

if scan_argument:
    print("\033[32mScanning %s\033[0m" % target_path)
    if options.recent:
        command_parts.extend(['--scan-recent', scan_argument])
    else:
        command_parts.extend(['--scan-all', scan_argument])

# update signatures
subprocess.call([MALDET_PATH, '--update'])

# Run the scan. os.execvp replaces this python process with maldet itself,
# recycling the PID as well.
os.execvp(command_parts[0], command_parts)


Zerion Mini Shell 1.0