MOON
Server: Apache
System: Linux smtp.modiva.org 3.10.0-862.14.4.el7.x86_64 #1 SMP Wed Sep 26 15:12:11 UTC 2018 x86_64
User: rtbrisc (1005)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //opt/microsoft/mdatp/tools/client_analyzer/python/mde_tools/exclude.py
import os
import time
import logging
from pathlib import Path

from .constants import LOGGER_NAME, EXLUSION_RULES
from . import syscalls
from .utils import run_with_output
from .machine import machine
from .mdatp import mdatp

log = logging.getLogger(LOGGER_NAME)

def reset_dispatch():
    auditd_pid = int(run_with_output("pgrep -f /sbin/auditd"))
    log.info(f"[>] auditd found with pid={auditd_pid}")
    run_with_output(f"kill -HUP {auditd_pid}")

def override_prompt(args):
    if os.path.exists(EXLUSION_RULES) and not args.override:
        override = input("The exclusion rule file already exist. Do you want to override it with new rules? [y/N] ").lower()
        if override =='y':
            args.override=True

def validate_exclusion_path(path):
    if not os.path.exists(path):
        log.warning(f'The path [{path}] doesn\'t exist and will not be added to the exlusion rules.')
        return False
    return True

def validate_pid(pid):
    if not os.path.exists(f'/proc/{pid}'):
        log.warning(f'The pid [{pid}] doesn\'t correspond to any running process. Will not be added to exlusion rules')
        return None

    pid_info = run_with_output(f'ps -o cmd --pid {pid}').split('\n')

    if len(pid_info) == 1: #process got killed
        return None
    return pid_info[1]

def create_exclusion_file(args):
    if args.arch and (args.arch == "32" or args.arch == "64"):
        ARCHITECTURE=f"b{args.arch}"
    else:
        ARCHITECTURE="b64"

    override_prompt(args)
    exclusions = ""
    syscall = syscalls.Syscalls64 if ARCHITECTURE == "b64" else syscalls.Syscalls32
    if args.exe:
        for e in args.exe:
            if not validate_exclusion_path(e):
                continue
            log.info(f"[>] setting an exe exclusion rule for {e}")
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.SOCKET} -S {syscall.CONNECT} -S {syscall.BIND} -S {syscall.ACCEPT} -S {syscall.ACCEPT4} -S {syscall.SETSOCKOPT} -F exe={e} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.RENAME} -S {syscall.RENAMEAT} -S {syscall.RENAMEAT2} -S {syscall.RMDIR} -S {syscall.UNLINK} -S {syscall.UNLINKAT} -S {syscall.PTRACE} -F exe={e} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.CHOWN} -S {syscall.FCHOWN} -S {syscall.FCHOWNAT} -S {syscall.CHMOD} -S {syscall.FCHMODAT} -S {syscall.FCHMOD} -S {syscall.BPF} -F exe={e} -k exclude\n"

    if args.pid:
        for p in args.pid:
            process = validate_pid(p)
            if not process:
                continue
            log.info(f"[>] setting a pid exclusion rule for pid {p} corresponding to process [{process}]")
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.SOCKET} -S {syscall.CONNECT} -S {syscall.BIND} -S {syscall.ACCEPT} -S {syscall.ACCEPT4} -S {syscall.SETSOCKOPT} -F pid={p} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.RENAME} -S {syscall.RENAMEAT} -S {syscall.RENAMEAT2} -S {syscall.RMDIR} -S {syscall.UNLINK} -S {syscall.UNLINKAT} -S {syscall.PTRACE} -F pid={p} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.CHOWN} -S {syscall.FCHOWN} -S {syscall.FCHOWNAT} -S {syscall.CHMOD} -S {syscall.FCHMODAT} -S {syscall.FCHMOD} -S {syscall.BPF} -F pid={p} -k exclude\n"

    if args.dir:
        for d in args.dir:
            if not validate_exclusion_path(d):
                continue
            log.info(f"[>] setting a path exclusion rule for path {d}")
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.RENAME} -S {syscall.RENAMEAT} -S {syscall.RENAMEAT2} -S {syscall.RMDIR} -S {syscall.UNLINK} -S {syscall.UNLINKAT} -S {syscall.PTRACE} -F dir={d} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.CHOWN} -S {syscall.FCHOWN} -S {syscall.FCHOWNAT} -S {syscall.CHMOD} -S {syscall.FCHMODAT} -S {syscall.FCHMOD} -S {syscall.BPF} -F dir={d} -k exclude\n"

    if args.exe_dir:
        for ed in args.exe_dir:
            if not validate_exclusion_path(ed[0]) or not validate_exclusion_path(ed[1]):
                continue
            log.info(f"[>] setting a exe and path exclusion rule for exe {ed[0]} and path {ed[1]}")
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.RENAME} -S {syscall.RENAMEAT} -S {syscall.RENAMEAT2} -S {syscall.RMDIR} -S {syscall.UNLINK} -S {syscall.UNLINKAT} -S {syscall.PTRACE} -F exe={ed[0]} -F dir={ed[1]} -k exclude\n"
            exclusions += f"-a exit,never -F arch={ARCHITECTURE} -S {syscall.CHOWN} -S {syscall.FCHOWN} -S {syscall.FCHOWNAT} -S {syscall.CHMOD} -S {syscall.FCHMODAT} -S {syscall.FCHMOD} -S {syscall.BPF} -F exe={ed[0]} -F dir={ed[1]} -k exclude\n"


    if args.syscall:
        log.info(f"[>] Setting a syscall exclusion rule for {args.syscall}")
        rule = ' '.join(f"-S {s}" for s in args.syscall)
        exclusions += f"-a exit,never -F arch={ARCHITECTURE} {rule} -k exclude\n"

    if exclusions:
        if args.override: # remove previous exclusion files
            exclusions = "-D -k exclude\n" + exclusions
        log.info('Adding the following rules:\n{0}\n{1}\n{0}'.format('*'*10, exclusions))
        with Path(EXLUSION_RULES).open('w' if args.override else 'a') as rules_fp:
            rules_fp.write(exclusions)
            mdatp.restart_auditd()

def remove_exclusion_file():
    file = EXLUSION_RULES
    if os.path.exists(file):
        log.info(f"[>] removing {file}")
        os.remove(file)
        mdatp.restart_auditd()
    else:
        log.error(f'--remove flag was chosen but {file} does not exist')

def set_auditd_qdepth(q_depth):
    config_file = '/etc/audit/auditd.conf'
    if os.path.exists('/sbin/audispd'):
        if os.path.exists('/etc/audisp'):
            config_file = '/etc/audisp/audispd.conf'
        else:
            log.warning(f'[>] audispd exists but /etc/audisp doesn\'t exist. Using [{config_file}]')

    with Path(config_file).open() as f:
        q_lines = [l.strip() for l in f if "q_depth" in l]

    if q_lines:
        # update q_depth:
        run_with_output(f"sudo sed -i s/q_depth = .*/q_depth = {q_depth}/g {config_file}")
        log.info(f"[>] q_depth updated to {q_depth}")
    else:
        # set q_depth:
        with open(config_file, "a") as f:
            f.write(f"q_depth = {q_depth}\n")
            log.info(f"[>] q_depth not defined, added q_depth = {q_depth}")

    reset_dispatch()

def exclude(args):
    if args.list:
        log.info(run_with_output("auditctl -l"))

    if args.stat:
        log.info(run_with_output("aureport -x --summary"))

    if args.exe or args.pid or args.dir or args.exe_dir or args.syscall:
        create_exclusion_file(args)

    if args.remove:
        remove_exclusion_file()

    if args.queue:
        set_auditd_qdepth(args.queue)