Linux ip-172-26-2-223 5.4.0-1018-aws #18-Ubuntu SMP Wed Jun 24 01:15:00 UTC 2020 x86_64
Apache
: 172.26.2.223 | : 3.22.117.210
Cant Read [ /etc/named.conf ]
8.1.13
www
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
BLACK DEFEND!
README
+ Create Folder
+ Create File
/
usr /
local /
maldetect.bk111626 /
internals /
[ HOME SHELL ]
Name
Size
Permission
Action
VERSION.hash
64
B
-rw-r--r--
compat.conf
3.83
KB
-rw-r--r--
functions
85.06
KB
-rw-r--r--
hexfifo.pl
1.38
KB
-rw-r--r--
hexstring.pl
644
B
-rw-r--r--
importconf
14.81
KB
-rw-r--r--
internals.conf
4.29
KB
-rw-r--r--
panel_alert.etpl
2.12
KB
-rw-r--r--
scan.etpl
2.29
KB
-rw-r--r--
tlog
2.44
KB
-rwxr-x---
Delete
Unzip
Zip
${this.title}
Close
Code Editor : functions
## # Linux Malware Detect v1.6.5 # (C) 2002-2023, R-fx Networks <proj@r-fx.org> # (C) 2023, Ryan MacDonald <ryan@r-fx.org> # This program may be freely redistributed under the terms of the GNU GPL v2 ## # lbreakifs() { if [ "$1" == "set" ]; then IFS=$(echo -en "\n\b") else unset IFS fi } prerun() { startdir=$(pwd); if [ ! "$(whoami)" == "root" ]; then if [ -z "$scan_user_access" ] || [ "$scan_user_access" == "0" ]; then args="$@" if [[ "$args" =~ "modsec" ]]; then echo "1 maldet: OK" exit fi header echo "public scanning is currently disabled (scan_user_access=0), please contact your system administrator to enable scan_user_access in $cnffile." exit 1 fi pub=1 user="$(whoami)" quardir="$userbasedir/$user/quar" sessdir="$userbasedir/$user/sess" tmpdir="$userbasedir/$user/tmp" scan_tmpdir_paths="" hits_history="$sessdir/hits.hist" quar_history="$sessdir/quarantine.hist" clean_history="$sessdir/clean.hist" suspend_history="$sessdir/suspend.hist" monitor_scanned_history="$sessdir/monitor.scanned.hist" if [ ! -d "$userbasedir/$user/tmp" ]; then header echo "public scanning is enabled (scan_user_access=1) but paths do not exist, please contact your system administrator to run '$0 --mkpubpaths' or wait for cron.pub to execute in ~10 minutes." exit 1 fi maldet_log="$userbasedir/$user/event_log" clamscan_log="$userbasedir/$user/clamscan_log" mkdir -p $quardir $sessdir $tmpdir 2> /dev/null chmod 711 $userbasedir 2> /dev/null touch $maldet_log 2> /dev/null chown -R ${user}.root $userbasedir/$user 2> /dev/null chmod 750 $userbasedir/$user $quardir $sessdir $tmpdir 2> /dev/null chmod 640 $maldet_log 2> /dev/null cd $tmpdir else echo $ver > $lmd_version_file fi if [ ! -d "$sigdir" ]; then mkdir -p $sigdir chmod 755 $sigdir fi if [ ! -d "$logdir" ]; then mkdir -p $logdir chmod 755 $logdir fi if [ ! -d "$tmpdir" ]; then mkdir -p $tmpdir chmod 755 $tmpdir else chmod 755 $tmpdir fi if [ ! -d "$sessdir" ]; then mkdir -p $sessdir chmod 750 $sessdir fi if [ ! -d "$quardir" ]; then mkdir -p $quardir chmod 750 $quardir fi if [ -z "$md5sum" ]; then header echo "could not find required binary md5sum, aborting." exit 1 fi if [ -z "$od" ]; then header echo "could not find required binary od, aborting." exit 1 fi if [ -z "$find" ]; then header echo "could not find required binary find, aborting." exit 1 fi if [ -z "$perl" ]; then header echo "could not find required binary perl, aborting." exit 1 fi if [ "$email_alert" == "1" ] && [ ! -f "$mail" ] && [ ! -f "$sendmail" ]; then email_alert=0 fi if [ ! -f "$sig_user_hex_file" ]; then touch $sig_user_hex_file chmod 644 $sig_user_hex_file fi if [ ! -f "$sig_user_md5_file" ]; then touch $sig_user_md5_file chmod 644 $sig_user_md5_file fi if [ "$scan_hexfifo" == "1" ]; then mkfifo=`which mkfifo 2> /dev/null` if [ ! -f "$mkfifo" ]; then scan_hexfifo=0 else if [ -f "$mkfifo" ] && [ ! -p "$hex_fifo_path" ]; then $mkfifo -m 666 $hex_fifo_path fi fi fi if [ "$user" == "root" ]; then $sed -i -e '/^$/d' $ignore_paths $ignore_sigs $ignore_inotify $ignore_file_ext fi if [ -z "$EDITOR" ]; then defedit=`which nano 2> /dev/null` if [ -z "$defedit" ]; then EDITOR=vi else EDITOR=nano fi fi if [ ! "$scan_cpunice" ]; then scan_cpunice=19 fi if [ ! "$scan_ionice" ]; then scan_ionice=6 fi if [ -f "$nice" ]; then nice_command="$nice -n $scan_cpunice" fi if [ -f "$ionice" ] && [ ! -d "/proc/vz" ]; then nice_command="$nice_command $ionice -c2 -n $scan_ionice" fi if [ -f "$cpulimit" ] && [ "$scan_cpulimit" -gt 2> /dev/null "0" ]; then max_cpulimit=$[$(grep -E -w processor /proc/cpuinfo -c)*100] if [ "$scan_cpulimit" -gt "$max_cpulimit" ]; then scan_cpulimit="0" else nice_command="$cpulimit -l $scan_cpulimit -- $nice_command" fi fi if [ -z "$cron_daily_scan" ]; then cron_daily_scan=1 fi } eout() { msg="$1" stdout="$2" appn=maldet if [ ! -d "$logdir" ]; then mkdir -p $logdir ; chmod 700 $logdir fi if [ ! -f "$maldet_log" ]; then touch $maldet_log fi if [ "$maldet_log_truncate" == "1" ]; then log_size=`$wc -l $maldet_log | awk '{print$1}'` if [ "$log_size" -ge "20000" ]; then trim=1000 printf "%s\n" "$trim,${log_size}d" w | ed -s $maldet_log 2> /dev/null fi fi if [ ! "$msg" == "" ]; then echo "$(date +"%b %d %Y %H:%M:%S") $(hostname -s) $appn($$): $msg" >> $maldet_log if [ ! -z "$stdout" ]; then echo "$appn($$): $msg" fi fi } trap_exit() { if [ "$svc" == "m" ]; then echo eout "{glob} monitor interrupt by user, sending kill." 1 monitor_kill exit 1 elif [ "$svc" == "a" ] || [ "$svc" == "r" ] || [ "$svc" == "f" ]; then echo gen_report if [ ! "$tot_hits" == "0" ]; then if [ "$email_ignore_clean" == "1" ] && [ ! "$tot_hits" == "$tot_cl" ]; then genalert file $nsess elif [ "$email_ignore_clean" == "0" ]; then genalert file $nsess fi fi mv $scan_session $nsess_hits 2> /dev/null rm -f $clamscan_results $find_results $runtime_hdb $runtime_hexstrings $runtime_ndb $scan_session $tmpdir/.find_killed.$scanid $tmpdir/.tmp* $tmpdir/.tmpf* $tmpf 2> /dev/null eout "{glob} scan interrupt by user, aborting scan..." 1 eout "{scan} scan report saved, to view run: maldet --report $datestamp.$$" 1 if [ "$quarantine_hits" == "0" ] && [ ! "$tot_hits" == "0" ]; then eout "{glob} quarantine is disabled! set quarantine_hits=1 in $cnffile or to quarantine results run: maldet -q $datestamp.$$" 1 fi exit fi } clean_exit() { mv -f $scan_session $nsess_hits 2> /dev/null rm -f $clamscan_results $find_results $list $runtime_hdb $runtime_hexstrings $runtime_ndb $scan_session $tmpdir/.find_killed.$scanid $tmpdir/.tmp* $tmpdir/.tmpf* $tmpf 0 2> /dev/null } detect_control_panel() { if [[ -d /usr/local/interworx ]]; then iworx_db_ps=$(ps -u iworx | grep iworx-db) iworx_web_ps=$(ps -u iworx-web | grep iworx-web) pex_script=$(readlink -e /usr/local/interworx/bin/listaccounts.pex) siteworx=$(which siteworx) # Check that Iworx services are running if [[ -z "$iworx_db_ps" || -z "$iworx_web_ps" ]]; then control_panel="error" eout "{panel} Interworx found, but not running. Panel user alerts will not be sent." # Verify pex script exists and is executable elif ! [[ -x "$pex_script" ]]; then control_panel="error" eout "{panel} Interworx found, but scripts are missing or not executable. Panel user alerts will not be sent." # Ensure /usr/bin/siteworx is executable elif ! [[ -x "$siteworx" ]]; then control_panel="error" eout "{panel} Interworx found, but Siteworx CLI is missing or not executable. Panel user alerts will not be sent." else control_panel="interworx" fi elif [[ -d /usr/local/cpanel ]]; then cpanel_ps=$(ps -u root | grep [c]psrvd) cpapi=$(which cpapi2) apitool=$(readlink -e ${cpapi}) # Ensure cpanel service is running if [[ -z ${cpanel_ps} ]]; then control_panel="error" eout "{panel} cPanel found, but services are not running. Panel user alerts will not be sent." # Verify apitool is executable elif ! [[ -x ${apitool} ]]; then control_panel="error" eout "{panel} cPanel found, but apitool is missing or not found. Panel user alerts will not be sent." else control_panel="cpanel" fi else control_panel="unknown" fi } get_panel_contacts() { panel="$1" user="$2" case "$panel" in "cpanel") if [ -f /var/cpanel/users/${user} ]; then contact_emails=$(awk -F '=' '/^CONTACTEMAIL/{print $2}' /var/cpanel/users/${user} | sed '/^ *$/d' | tr '\n' ',' | sed 's/,$//') else contact_emails=$(cpapi2 --user=herpaderpadoo --output=xml CustInfo contactemails | grep -o 'value>.*</value' | sed -r 's,(</)?value>?,,g;/^$/d' | tr '\n' ',' | sed 's/,$//') fi ;; "interworx") master_domain=$(/usr/local/interworx/bin/listaccounts.pex | grep "${user}" | awk '{print $2}') contact_emails=$(/usr/bin/siteworx -un --login_domain ${master_domain} -c Users -a listUsers -o yaml | awk '/email:/{print $2}' | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /') ;; esac } get_remote_file() { # $1 = URI, $2 = local service identifier, $3 boolean verbose get_uri="$1" service="$2" verbose="$3" save_path="$4" unset return_file if [ "$hscan" ]; then unset verbose fi if [ -z "$get_uri" ]; then eout "{internal} missing or invalid URI passed to get_remote_file()" 1 break fi if [ -z "$service" ]; then svc="internal" else svc="$service" fi if [ -f "$curl" ] || [ -f "/usr/bin/curl" ]; then get_type="curl" if [ -z "$curl" ]; then get_bin="/usr/bin/curl" else get_bin="$curl" fi elif [ -f "$wget" ] || [ -f "/usr/bin/wget" ]; then get_type="wget" if [ -z "$wget" ]; then get_bin="/usr/bin/wget" else get_bin="$wget" fi else eout "{internal} could not find curl or wget binaries for remote file downloads, fatal error!" exit 1 fi if [ "$lmd_referer" ] && [ "$get_type" == "curl" ]; then id="--referer ${lmd_referer}:curl" elif [ "$lmd_referer" ] && [ "$get_type" == "wget" ]; then id="--referer=${lmd_referer}:wget" fi if [ "$get_type" == "curl" ]; then if [ "$web_proxy" ]; then get_proxy_arg="-x http://$web_proxy" fi get_opts="-s $get_proxy_arg --connect-timeout $remote_uri_timeout --retry $remote_uri_retries $id" get_output_arg='-o' elif [ "$get_type" == "wget" ]; then if [ "$web_proxy" ]; then get_proxy_arg="-e http_proxy=$web_proxy -e https_proxy=$web_proxy" fi get_opts="-q $get_proxy_arg --timeout=$remote_uri_timeout --tries=$remote_uri_retries $id" get_output_arg='-O' fi if [ "$save_path" ]; then tmpf="$save_path" else tmpf="$tmpdir/.tmpf_get.${RANDOM}" touch $tmpf ; chmod 600 $tmpf get_file=`echo "$get_uri" | tr '/' '\n' | tail -n1` fi $get_bin $get_opts "$get_uri" $get_output_arg "$tmpf" || get_return=$? if [ ! -f "$tmpf" ] || [ ! -s "$tmpf" ]; then eout "{$svc} could not download $get_uri, please try again later." $verbose unset return_file else eout "{$svc} downloaded $get_uri" return_file="$tmpf" fi } import_user_sigs() { if [ "$import_custsigs_md5_url" ]; then get_remote_file "$import_custsigs_md5_url" "importsigs" "1" if [ -f "$return_file" ]; then cp -f $return_file $sig_user_md5_file eout "{importsigs} imported custom signature data from $import_custsigs_md5_url" fi fi if [ "$import_custsigs_hex_url" ]; then get_remote_file "$import_custsigs_hex_url" "importsigs" "1" if [ -f "$return_file" ]; then cp -f $return_file $sig_user_hex_file eout "{importsigs} imported custom signature data from $import_custsigs_hex_url" fi fi } import_conf() { current_utime=`date +"%s"` if [ -z "$import_config_expire" ]; then import_config_expire=43200 fi if [ -f "$sessdir/.import_conf.utime" ]; then import_utime=`cat $sessdir/.import_conf.utime` if [ -z "$import_utime" ]; then import_utime="0" fi import_diff=$[current_utime-import_utime] if [ "$import_diff" -lt "$import_config_expire" ]; then import_config_skip="1" eout "{importconf} configuration expire value has not lapsed (${import_diff}/${import_config_expire}), using cache." import_conf_cached=1 fi fi if [ "$import_config_url" ]; then if [ -z "$import_config_skip" ]; then get_remote_file "$import_config_url" "importconf" "1" if [ -f "$return_file" ]; then cp -f $return_file $sessdir/.import_conf.cache echo "$current_utime" > $sessdir/.import_conf.utime fi fi if [ -f "$sessdir/.import_conf.cache" ]; then source $cnf source $intcnf source $sessdir/.import_conf.cache if [ "$import_conf_cached" ]; then eout "{importconf} imported configuration from $import_config_url (cached)" else eout "{importconf} imported configuration from $import_config_url" fi fi fi } clamav_linksigs() { cpath="$1" if [ -d "$cpath" ]; then rm -f $cpath/rfxn.{hdb,ndb,yara} 2> /dev/null ; cp -f $sigdir/rfxn.{hdb,ndb,yara} $cpath/ 2> /dev/null rm -f $cpath/lmd.user.* 2> /dev/null ; cp -f $sigdir/lmd.user.ndb $sigdir/lmd.user.hdb $cpath/ 2> /dev/null fi } usage_short() { cat <<EOF signature set: $sig_version usage maldet [-h|--help] [-a|--scan-all PATH] [-r|--scan-recent PATH DAYS] [-f|--file-list PATH] [-i|--include-regex] [-x|--exclude-regex] [-b|--background] [-m|--monitor] [-k|--kill-monitor] [-c|--checkout] [-q|--quarantine] [-s|--restore] [-n|--clean] [-l|--log] [-e|--report] [-u|--update-sigs] [-d|--update-ver] EOF } usage_long() { cat<<EOF signature set: $sig_version usage $0 [ OPTION ] -b, --background Execute operations in the background, ideal for large scans e.g: maldet -b -r /home/?/public_html 7 -u, --update-sigs [--force] Update malware detection signatures from rfxn.com -d, --update-ver [--force] Update the installed version from rfxn.com -f, --file-list Scan files or paths defined in line spaced file e.g: maldet -f /root/scan_file_list -r, --scan-recent PATH DAYS Scan files created/modified in the last X days (default: 7d, wildcard: ?) e.g: maldet -r /home/?/public_html 2 -a, --scan-all PATH Scan all files in path (default: /home, wildcard: ?) e.g: maldet -a /home/?/public_html -i, --include-regex REGEX Include paths/files from file list based on supplied posix-egrep regular expression. e.g: To include only paths named wp-content and files ending in .php: --include-regex ".*/wp-content/.*|.*.php$" -x, --exclude-regex REGEX Exclude paths/files from file list based on supplied posix-egrep regular expression. e.g: To exclude paths containing 'wp-content/w3tc/' and core files: --exclude-regex ".*wp-content/w3tc/.*|.*core.[0-9]+$" -m, --monitor USERS|PATHS|FILE|RELOAD Run maldet with inotify kernel level file create/modify monitoring If USERS is specified, monitor user homedirs for UID's > 500 If FILE is specified, paths will be extracted from file, line spaced If PATHS are specified, must be comma spaced list, NO WILDCARDS! e.g: maldet --monitor users e.g: maldet --monitor /root/monitor_paths e.g: maldet --monitor /home/mike,/home/ashton -k, --kill-monitor Terminate inotify monitoring service -c, --checkout FILE Upload suspected malware to rfxn.com for review & hashing into signatures -l, --log View maldet log file events -e, --report SCANID email View scan report of most recent scan or of a specific SCANID and optionally e-mail the report to a supplied e-mail address e.g: maldet --report e.g: maldet --report list e.g: maldet --report 050910-1534.21135 e.g: maldet --report SCANID user@domain.com -s, --restore FILE|SCANID Restore file from quarantine queue to orginal path or restore all items from a specific SCANID e.g: maldet --restore $varlibpath/quarantine/config.php.23754 e.g: maldet --restore 050910-1534.21135 -q, --quarantine SCANID Quarantine all malware from report SCANID e.g: maldet --quarantine 050910-1534.21135 -n, --clean SCANID Try to clean & restore malware hits from report SCANID e.g: maldet --clean 050910-1534.21135 -U, --user USER Set execution under specified user, ideal for restoring from user quarantine or to view user reports. e.g: maldet --user nobody --report e.g: maldet --user nobody --restore 050910-1534.21135 -co, --config-option VAR1=VALUE,VAR2=VALUE,VAR3=VALUE Set or redefine the value of $cnffile config options e.g: maldet --config-option email_addr=you@domain.com,quarantine_hits=1 -p, --purge Clear logs, quarantine queue, session and temporary data. --web-proxy IP:PORT Enable use of HTTP/HTTPS proxy for all remote URL calls. EOF } clean() { file="$1" file_signame="$2" file_owner="$3" file_chmod="$4" file_size="$5" file_md5="$6" sh_hitname=`echo $hitname | sed -r -e 's/\{(HEX|MD5|CAV|YARA)\}//' -e 's/\.[0-9]+$//'` if [ -d "$cldir" ] && [ "$quarantine_clean" == "1" ] && [ "$quarantine_hits" == "1" ] && [ -f "$file" ]; then if [ -f "$cldir/$sh_hitname" ] || [ -f "$cldir/custom.$sh_hitname" ] && [ -f "${file}.info" ]; then file_path=`grep -E -v '\#' ${file}.info | cut -d':' -f9` eout "{clean} restoring $file for cleaning attempt" 1 restore "$file" >> /dev/null 2>&1 if [ -f "$cldir/$sh_hitname" ]; then eout "{clean} attempting to clean $file_path with $sh_hitname rule" 1 $cldir/$sh_hitname "$file_path" "$file_signame" "$file_owner" "$file_chmod" "$file_size" "$file_md5" fi if [ -f "$cldir/custom.$sh_hitname" ]; then eout "{clean} attempting to clean $file_path with custom.$sh_hitname rule" 1 $cldir/custom.$sh_hitname "$file_path" "$file_signame" "$file_owner" "$file_chmod" "$file_size" "$file_md5" fi eout "{clean} rescanning $file_path for malware hits" 1 clean_state="1" scan_stage1 "$file_path" >> /dev/null 2>&1 unset clean_state if [ -f "$file_path" ]; then echo "$file_path" >> $sessdir/clean.$$ echo "$file_path" >> $clean_history eout "{clean} clean successful on $file_path" 1 else eout "{clean} clean failed on $file_path and returned to quarantine" 1 fi elif [ -f "$cldir/$sh_hitname" ] || [ -f "$cldir/custom.$sh_hitname" ] && [ -f "$file" ]; then file_path="$file" if [ -f "$cldir/$sh_hitname" ]; then eout "{clean} attempting to clean $file with $sh_hitname rule" 1 $cldir/$sh_hitname "$file_path" fi if [ -f "$cldir/custom.$sh_hitname" ]; then eout "{clean} attempting to clean $file with custom.$sh_hitname rule" 1 $cldir/custom.$sh_hitname "$file_path" fi eout "{clean} scanning $file for malware hits" clean_state="1" unset clean_failed scan_stage1 "$file_path" 1 >> /dev/null 2>&1 unset clean_state if [ "$clean_failed" == "1" ]; then eout "{clean} clean failed on $file" 1 else echo "$file" >> $sessdir/clean.$$ echo "$file_path" >> $clean_history eout "{clean} clean successful on $file" 1 fi else eout "{clean} could not find clean rule for hit $sh_hitname or file $file no longer exists." 1 fi else if [ "$quarantine_clean" == "1" ] && [ "$quarantine_hits" == "1" ]; then eout "file path error on $file, aborting." exit else eout "quarantine_clean and quarantine_hits are disabled; skipped file $file" fi fi } quar_get_filestat() { fstat="$1" if [ -f "$fstat" ]; then # owner:group:mode:size(b):md5:atime(epoch):mtime(epoch):ctime(epoch):file(path) file_owner=`grep -E -v '\#' "$fstat" | awk -F':' '{print$1}'` file_group=`grep -E -v '\#' "$fstat" | awk -F':' '{print$2}'` file_mode=`grep -E -v '\#' "$fstat" | awk -F':' '{print$3}'` file_size=`grep -E -v '\#' "$fstat" | awk -F':' '{print$4}'` md5_hash=`grep -E -v '\#' "$fstat" | awk -F':' '{print$5}'` file_atime=`grep -E -v '\#' "$fstat" | awk -F':' '{print$6}'` file_mtime=`grep -E -v '\#' "$fstat" | awk -F':' '{print$7}'` file_ctime=`grep -E -v '\#' "$fstat" | awk -F':' '{print$8}'` file_path=`grep -E -v '\#' "$fstat" | cut -d':' -f9` fi } restore() { file="$1" fname=`basename "$file"` if [ -f "$quardir/$file" ] && [ -f "$quardir/${file}.info" ]; then quar_get_filestat "$quardir/${file}.info" chown ${file_owner}.${file_group} "$quardir/$file" >> /dev/null 2>&1 chmod $file_mode "$quardir/$file" >> /dev/null 2>&1 mv -f "$quardir/$file" "$file_path" touch -m --date=@${file_mtime} "$file_path" eout "{restore} quarantined file '$file' restored to '$file_path'" 1 elif [ -f "$file" ] && [ -f "${file}.info" ]; then quar_get_filestat "${file}.info" chown ${file_owner}.${file_group} "$file" >> /dev/null 2>&1 chmod $file_mode "$file" >> /dev/null 2>&1 mv -f "$file" "$file_path" touch -m --date=@${file_mtime} "$file_path" eout "{restore} quarantined file '$file' restored to '$file_path'" 1 else eout "{restore} '$file' is not eligible for restore or could not be found" 1 fi } restore_hitlist() { hitlist="$sessdir/session.hits.$1" if [ -f "$hitlist" ]; then lbreakifs set is_autoquar=`tail -n1 $hitlist | awk -F'>' '{print$2}' | grep -E -v '^$' | sed 's/.//'` if [ "$is_autoquar" ]; then for file in `cat $hitlist | cut -d':' -f2 | cut -d'>' -f2 | sed 's/.//'`; do if [ -f "$file" ]; then restore "$file" fi done elif [ ! "$is_autoquar" ]; then for file in `cat $hitlist | cut -d':' -f2 | sed 's/.//'`; do quar_file=`cat $quar_history | grep -E -w "$file" | cut -d':' -f8 | tail -n1` restore "$quar_file" done else eout "{restore} could not find a valid hit list to restore." 1 fi lbreakifs unset fi } clean_hitlist() { if [ "$quarantine_clean" == "0" ] || [ "$quarantine_hits" == "0" ]; then eout "{clean} quarantine_clean and/or quarantine_hits are disabled, nothing to do here." 1 exit 0 fi scanid="$1" hitlist="$sessdir/session.hits.$scanid" if [ -f "$hitlist" ]; then is_quared=`egrep '=>' $hitlist` if [ ! "$is_quared" ]; then lbreakifs set for file in `cat $hitlist | cut -d':' -f2 | sed 's/.//'`; do get_filestat "$file" 1 hitname=`cat $hitlist | grep $file | awk '{print$1}'` if [ ! "$md5_hash" ]; then md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` fi clean "$file" "$hitname" "$file_owner.$file_group" "$file_mode" "$file_size" "$md5_hash" done lbreakifs unset else lbreakifs set for file in `cat $hitlist | cut -d'>' -f2 | sed 's/.//'`; do quar_get_filestat "${file}.info" 1 hitname=`cat $hitlist | grep $file | awk '{print$1}'` if [ ! "$md5_hash" ]; then md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` fi clean "$file" "$hitname" "$file_owner.$file_group" "$file_mode" "$file_size" "$md5_hash" done lbreakifs unset fi else eout "{clean} invalid scanid $scanid or unknown error, aborting." 1 exit fi } view_report() { rid="$1" if [ "$rid" == "list" ]; then tmpf="$tmpdir/.areps$$" for file in `ls $sessdir/session.[0-9]* 2> /dev/null`; do SCANID=`cat $file | grep "SCAN ID" | sed 's/SCAN ID/SCANID/'` FILES=`cat $file | grep "TOTAL FILES" | sed 's/TOTAL //'` HITS=`cat $file | grep "TOTAL HITS" | sed 's/TOTAL //'` CLEAN=`cat $file | grep "TOTAL CLEANED" | sed 's/TOTAL //'` TIME=`cat $file | grep -E "^TIME|^STARTED" | sed -e 's/TIME: //' -e 's/STARTED: //' | awk '{print$1,$2,$3,$4}'` TIME_U=`date -d "$TIME" "+%s" 2> /dev/null` ETIME=`cat $file | grep "ELAPSED" | awk '{print$1,$2}' | sed 's/ELAPSED/RUNTIME/'` if [ -z "$ETIME" ]; then ETIME="RUNTIME: unknown" fi if [ ! -z "$SCANID" ] && [ ! -z "$TIME" ]; then clean_zero=`echo $CLEAN | awk '{print$2}'` if [ -z "$clean_zero" ]; then CLEAN="CLEANED: 0" fi echo "$TIME_U | $TIME | $SCANID | $ETIME | $FILES | $HITS | $CLEAN" >> $tmpf fi done if [ -f "$tmpf" ]; then if [ "$OSTYPE" == "FreeBSD" ]; then cat $tmpf | sort -k1 -n | cut -d'|' -f2-7 | column -t | more else cat $tmpf | sort -k1 -n | tac | cut -d'|' -f2-7 | column -t | more fi rm -f $tmpf 2> /dev/null exit 0 else echo "error no report data found" exit 1 fi fi if [ -f "$sessdir/session.$rid" ] && [ ! -z "$(echo $2 | grep '\@')" ]; then if [ -f "$mail" ]; then cat $sessdir/session.$rid | $mail -s "$email_subj" "$2" elif [ -f "$sendmail" ]; then if ! grep -q "SUBJECT: " "$sessdir/session.$rid"; then echo -e "SUBJECT: $email_subj\n$(cat $sessdir/session.$rid)" > $sessdir/session.$rid fi cat $sessdir/session.$rid | $sendmail -t "$2" else eout "{scan} no \$mail or \$sendmail binaries found, e-mail alerts disabled." exit fi eout "{report} report ID $rid sent to $2" 1 exit fi if [ "$rid" == "" ] && [ -f "$sessdir/session.last" ]; then rid=`cat $sessdir/session.last` if [ "$2" == "dump" ]; then cat $sessdir/session.$rid exit fi $EDITOR $sessdir/session.$rid elif [ -f "$sessdir/session.$rid" ]; then if [ "$2" == "dump" ]; then cat $sessdir/session.$rid exit fi $EDITOR $sessdir/session.$rid else echo "{report} no report found, aborting." exit fi } dump_report() { rid="$1" if [ "$rid" == "" ]; then rid=`cat $sessdir/session.last` fi view_report $rid dump } view() { echo "Viewing last 50 lines from $maldet_log:" tail -n 50 $maldet_log } purge() { :> $maldet_log log_size=`$wc -l $inotify_log | awk '{print$1}'` if [ "$inotify_trim" ]; then trim=$(($log_size - 1000)) log_chars=`printf "%s\n" "1,${trim}p" | ed -s $inotify_log 2> /dev/null | wc -c` tlog_new=$(( `cat $inspath/tmp/inotify` - $log_chars )) echo $tlog_new > $inspath/tmp/inotify printf "%s\n" "1,${trim}d" w | ed -s $inotify_log 2> /dev/null eout "{mon} inotify log file trimmed" fi rm -f $tmpdir/* $quardir/* $sessdir/* 2> /dev/null eout "{glob} logs and quarantine data cleared by user request (-p)" 1 } quarantine_suspend_user() { file="$1" get_filestat "$file" user="$file_owner" user_id=`id -u $user` if [ ! "$user" == "" ] && [ "$user_id" -ge "$quarantine_suspend_user_minuid" ]; then if [ -f "/scripts/suspendacct" ]; then if [ ! -f "/var/cpanel/suspended/$user" ]; then /scripts/suspendacct $user "maldet --report $datestamp.$$" >> /dev/null 2>&1 eout "{quar} account $user cpanel suspended" 1 echo "$user" >> $sessdir/suspend.users.$$ echo "$user" >> $suspend_history fi else if [ "$(grep $user /etc/passwd | cut -d':' -f7 | grep /bin/false)" == "" ]; then /usr/sbin/usermod -s /bin/false $user >> /dev/null 2>&1 eout "{quar} account $user suspended; set 'usermod -s /bin/false'" echo "$user" >> $sessdir/suspend.users.$$ echo "$user" >> $suspend_history fi fi fi } get_filestat() { file="$1" times="$2" if [ "$OSTYPE" == "FreeBSD" ]; then file_owner=`$stat -f '%Su' "$file"` file_group=`$stat -f '%Sg' "$file"` file_mode=`$stat -f '%p' "$file" | sed 's/^.//'` file_size=`$stat -f '%Z' "$file"` md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` if [ "$times" ]; then # atime mtime ctime (since epoch) file_times=`$stat -f '%a:%m:%c' "$file"` fi else file_owner=`$stat -c '%U' "$file"` file_group=`$stat -c '%G' "$file"` file_mode=`$stat -c '%a' "$file"` file_size=`$stat -c '%s' "$file"` md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` if [ "$times" ]; then # atime mtime ctime (since epoch) file_times=`$stat -c '%X:%Y:%Z' "$file"` fi fi } record_hit() { file="$1" hitname="$2" if [ -f "$file" ]; then get_filestat "$file" 1 file_name=`basename "$file" 2> /dev/null` if [ ! "$md5_hash" ]; then md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` fi eout "{hit} malware hit $hitname found for $file" echo "${utime}:${hostid}:${hitname}:${md5_hash}:${file_size}:${file_owner}.${file_group}:${file_mode}:${file_times}:${file}" >> $hits_history fi } quarantine() { file="$1" hitname="$2" file_name=`basename "$file"` if [ -f "$file" ] && [ -d "$quardir" ]; then if [ "$quarantine_hits" == "1" ]; then file_namewc=`echo $file_name | $wc -m` rnd="${RANDOM}${RANDOM}" if [ "$quarantine_suspend_user" == "1" ]; then quarantine_suspend_user "$file" fi chattr -ia "$file" mv "$file" "$quardir/$file_name.$rnd" touch --no-create "$quardir/$file_name.$rnd" if [ "$pub" == "1" ]; then chmod 400 "$quardir/$file_name.$rnd" else chmod 000 "$quardir/$file_name.$rnd" chown root.root "$quardir/$file_name.$rnd" fi echo -e "# owner:group:mode:size(b):md5:atime(epoch):mtime(epoch):ctime(epoch):file(path)\n$file_owner:$file_group:$file_mode:$file_size:$md5_hash:$file_times:$file" > $quardir/$file_name.$rnd.info eout "{quar} malware quarantined from '$file' to '$quardir/$file_name.$rnd'" echo "$utime:$hitname:$file:$file_owner:$file_group:$md5_hash:$file_size:$quardir/$file_name.$rnd" >> $quar_history if [ ! -z "$scan_session" ]; then echo "$hitname : $file => $quardir/$file_name.$rnd" >> $scan_session fi if [ "$quarantine_clean" == "1" ] && [ ! "$clean_state" == "1" ]; then unset clean_state clean "$quardir/$file_name.$rnd" "$hitname" "$file_owner.$file_group" "$file_mode" "$file_size" "$md5_hash" "$file" fi else if [ ! -z "$scan_session" ]; then echo "$hitname : $file" >> $scan_session fi fi else eout "{quar} fatal error handling '$file'" fi } quar_hitlist() { hitlist="$sessdir/session.hits.$1" if [ -f "$hitlist" ]; then lbreakifs set for file in `cat $hitlist | cut -d':' -f2 | sed 's/.//'`; do if [ -f "$file" ]; then get_filestat "$file" 1 file_name=`basename "$file"` file_namewc=`echo "$file_name" | $wc -m` file_hitname=`egrep -m1 "$file" $hitlist | awk '{print$1}'` if [ ! "$md5_hash" ]; then md5_hash=`eval $md5sum \"$file\" | awk '{print$1}'` fi if [ "$pub" == "1" ]; then chattr -ia "$file" >> /dev/null 2>&1 chmod 400 "$file" else chattr -ia "$file" chmod 000 "$file" fi rnd="${RANDOM}${RANDOM}" mv "$file" "$quardir/$file_name.$rnd" touch --no-create "$quardir/$file_name.$rnd" echo -e "# owner:group:mode:size(b):md5:atime(epoch):mtime(epoch):ctime(epoch):file(path)\n$file_owner:$file_group:$file_mode:$file_size:$md5_hash:$file_times:$file" > $quardir/$file_name.$rnd.info eout "{quar} malware quarantined from '$file' to '$quardir/$file_name.$rnd'" 1 echo "$utime:$file_hitname:$file:$file_owner:$file_group:$md5_hash:$file_size:$quardir/$file_name.$rnd" >> $quar_history if [ "$quarantine_suspend_user" == "1" ]; then quarantine_suspend_user "$file" fi if [ "$quarantine_clean" == "1" ] && [ ! "$clean_state" == "1" ]; then unset clean_state hitname=`cat $hitlist | grep $file | awk '{print$1}'` clean "$quardir/$file_name.$rnd" "$hitname" "$file_owner.$file_group" "$file_mode" "$file_size" "$md5_hah" "$file" fi fi done lbreakifs unset else echo "{quar} invalid quarantine hit list, aborting." exit fi } clamselector() { scan_max_filesize=`cat $sig_md5_file | cut -d':' -f2 | sort -n | tail -n1` if [ "$scan_max_filesize" -gt "1" 2> /dev/null ]; then scan_max_filesize=$[scan_max_filesize+1] clamscan_max_filesize="${scan_max_filesize}" scan_max_filesize="${scan_max_filesize}c" else scan_max_filesize="2048k" clamscan_max_filesize="2592000" fi if [ "$scan_clamscan" == "1" ]; then trim_log $clamscan_log 10000 1 for dpath in $clamav_paths; do if [ -f "${dpath}/main.cld" ] || [ -f "${dpath}/main.cvd" ]; then clamav_db="-d $dpath" fi done isclamd=`pgrep -x clamd 2> /dev/null` isclamd_root=`pgrep -x -u root clamd 2> /dev/null` if [ "$scan_clamd_remote" == "1" ] && [ -f "$remote_clamd_config" ]; then clamd=1 clambin="clamdscan" clamopts="--fdpass -c $remote_clamd_config" elif [ "$isclamd" ] && [ "$isclamd_root" ]; then clamd=1 clambin="clamdscan" clamopts="$clamdscan_extraopts" elif [ "$isclamd" ] && [ ! "$isclamd_root" ]; then clamd=1 clambin="clamdscan" clamopts="--fdpass $clamdscan_extraopts" else clambin="clamscan" clamopts="$clamscan_extraopts --max-filesize=$clamscan_max_filesize --max-scansize=$[clamscan_max_filesize*2] -d $runtime_hdb -d $runtime_ndb $clamav_db -r" if [ "$monitor_mode" ]; then inotify_sleep="120" eout "{mon} warning clamd service not running; force-set monitor mode file scanning to every 120s" fi fi if [ -f "/usr/local/cpanel/3rdparty/bin/$clambin" ]; then clamscan="/usr/local/cpanel/3rdparty/bin/$clambin" elif [ -f "$(which $clambin 2> /dev/null)" ]; then clamscan=`which $clambin 2> /dev/null` else scan_clamscan="0" fi if [ "$clamd" ] && [ "$scan_clamscan" == "1" ]; then ## test clamdscan for errors as not all 'running' instances of clamd are indicative of working setup if [ "$scan_clamd_remote" == "1" ] && [ -f "$remote_clamd_config" ]; then try=0 while [ $try -le $remote_clamd_max_retry ]; do clamd_test=`$clamscan $clamopts --fdpass --quiet --no-summary /etc/passwd 2> /dev/null || echo $?` if [ "$clamd_test" = "2" ]; then ((try++)) sleep $remote_clamd_retry_sleep else break fi done else clamd_test=`$clamscan --fdpass --quiet --no-summary /etc/passwd 2> /dev/null || echo $?` fi if [ ! -z "$clamd_test" ]; then clamd=0 clambin="clamscan" clamopts="$clamscan_extraopts --max-filesize=$clamscan_max_filesize --max-scansize=$[clamscan_max_filesize*2] -d $runtime_hdb -d $runtime_ndb $clamav_db -r" if [ -f "/usr/local/cpanel/3rdparty/bin/$clambin" ]; then clamscan="/usr/local/cpanel/3rdparty/bin/$clambin" elif [ -f "$(which $clambin 2> /dev/null)" ]; then clamscan=`which $clambin 2> /dev/null` else scan_clamscan="0" fi fi fi fi } scan() { scan_start_hr=`date +"%b %e %Y %H:%M:%S %z"` scan_start=`date +"%s"` lbreakifs set spath=$(for i in `echo "$1" | tr '?' '*' | tr ',' '\n'`; do echo "\"$(printf %q $i)\""; done | tr '\n' ' ' | sed 's/.$//') lbreakifs unset days="$2" scanid="$datestamp.$$" if [ "$file_list" ]; then spath="\"$file_list\"" elif [ ! -f "$find" ]; then eout "{scan} could not locate find command" 1 clean_exit exit 1 fi if [ -f "$spath" ] && [ -z "$file_list" ]; then single_filescan=1 fi if [ ! -f "$sig_md5_file" ]; then eout "{scan} required signature file not found ($sig_md5_file), try running -u|--update, aborting!" 1 clean_exit exit 1 fi if [ ! -f "$sig_hex_file" ]; then eout "{scan} required signature file not found ($sig_hex_file), try running -u|--update, aborting!" 1 clean_exit exit 1 fi if [ ! -f "$ignore_paths" ]; then touch $ignore_paths chmod 640 $ignore_paths elif [ ! -f "$ignore_sigs" ]; then touch $ignore_sigs chmod 640 $ignore_sigs fi if [ ! "$days" == "all" ] && [ -z "$file_list" ]; then val=`echo $days | grep "[[:alpha:]]"` if [ ! -z "$val" ]; then eout "{scan} days value must be numeric value in the range of 1 - 90, reverting to default (7)." 1 days=7 elif [ "$days" -gt "90" ]; then eout "{scan} days value must be numeric value in the range of 1 - 90, reverting to default (7)." 1 days=7 fi fi if [ ! "${spath:1:1}" = "/" ]; then eout "{scan} must use absolute path, provided relative path $spath" 1 exit fi scan_session="$tmpdir/.sess.$$" find_results="$tmpdir/.find.$$" touch $find_results touch $scan_session sigignore gensigs if [ "$scan_clamscan" == "1" ]; then clamselector fi hex_sigs=`$wc -l $sig_hex_file | awk '{print$1}'` md5_sigs=`$wc -l $sig_md5_file | awk '{print$1}'` yara_sigs=`grep -E -c ^rule $sig_yara_file | awk '{print$1}'` user_hex_sigs=`$wc -l $sig_user_hex_file | awk '{print$1}'` user_md5_sigs=`$wc -l $sig_user_md5_file | awk '{print$1}'` user_sigs=$[user_hex_sigs+user_md5_sigs] tot_sigs=$[md5_sigs+hex_sigs+user_hex_sigs+user_md5_sigs+yara_sigs] if [ -z "$hscan" ]; then eout "{scan} signatures loaded: $tot_sigs ($md5_sigs MD5 | $hex_sigs HEX | $yara_sigs YARA | $user_sigs USER)" 1 fi if [ -f "$ignore_file_ext" ]; then if [ ! "$(cat $ignore_file_ext)" == "" ]; then for i in `cat $ignore_file_ext`; do if [ "$ignore_fext" == "" ]; then ignore_fext="-not -iname \"*$i\"" else ignore_fext="$ignore_fext -not -iname \"*$i\"" fi done fi fi if [ "$scan_ignore_root" == "1" ]; then ignore_root="-not -uid 0 -not -gid 0" fi if [ "$scan_ignore_user" ]; then for i in `echo $scan_ignore_user | tr ', ' ' '`; do if [ "$ignore_user" == "" ]; then ignore_user="-not -user $i" else ignore_user="$ignore_user -not -user $i" fi done fi if [ "$scan_ignore_group" ]; then for i in `echo $scan_ignore_group | tr ', ' ' '`; do if [ "$ignore_group" == "" ]; then ignore_group="-not -group $i" else ignore_group="$ignore_group -not -group $i" fi done fi if [ "$scan_tmpdir_paths" ] && [ -z "$hscan" ] && [ -z "$single_filescan" ]; then spath_tmpdirs="$scan_tmpdir_paths" fi if [ "$file_list" ]; then cat $file_list | grep -E -vf $ignore_paths > $find_results else if [ "$single_filescan" ]; then find_recentops="" elif [ "$days" == "all" ]; then if [ -z "$hscan" ]; then eout "{scan} building file list for $hrspath, this might take awhile..." 1 fi find_recentops="" else rscan=1 if [ -z "$hscan" ]; then eout "{scan} building file list for $hrspath of new/modified files from last $days days, this might take awhile..." 1 fi find_recentopts="\( -mtime -$days -o -ctime -$days \)" fi if [ -z "$scan_find_timeout" ];then scan_find_timeout=0 fi if [ "$scan_find_timeout" -ge "60" ]; then echo -e "sleep $scan_find_timeout\ntouch $tmpdir/.find_killed.$scanid\npkill -f lmd_find" > $tmpdir/.lmd_find_sleep.$$ sh -c "sh $tmpdir/.lmd_find_sleep.$$ >> /dev/null 2>&1 &" >> /dev/null 2>&1 & rm -f $tmpdir/.lmd_find_sleep.$$ 2> /dev/null eout "{scan} setting maximum execution time for 'find' file list: ${scan_find_timeout}sec" 1 fi if [ -z "$hscan" ]; then eout "{scan} setting nice scheduler priorities for all operations: cpunice $scan_cpunice , ionice $scan_ionice" 1 fi file_list_start=`date +"%s"` tmpscandir="$tmpdir/scan.$RANDOM" mkdir -p "$tmpscandir" ; chmod 700 $tmpscandir ; cd $tmpscandir eout "{scan} executed eval $nice_command $find $spath $spath_tmpdirs -maxdepth $scan_max_depth $find_opts -type f $find_recentopts -size +${scan_min_filesize}c -size -$scan_max_filesize $include_regex -not -perm 000 $exclude_regex $ignore_fext $ignore_root $ignore_user $ignore_group" eval $nice_command $find /lmd_find/ $(echo $spath) $spath_tmpdirs -maxdepth $scan_max_depth $find_opts -type f $find_recentopts -size +${scan_min_filesize}c -size -$scan_max_filesize $include_regex -not -perm 000 $exclude_regex $ignore_fext $ignore_root $ignore_user $ignore_group 2> /dev/null | grep -E -vf $ignore_paths > $find_results cd $tmpdir rm -rf $tmpscandir if [ "$rscan" = "1" ] && [ "$scan_export_filelist" == "1" ]; then rm -f $tmpdir/.find_results.* 2> /dev/null ; cp $find_results $tmpdir/.find_results.shared.$$ 2> /dev/null ln -fs $tmpdir/.find_results.shared.$$ $tmpdir/find_results.last 2> /dev/null fi file_list_end=`date +"%s"` file_list_et=$[file_list_end-file_list_start] if [ -f "$tmpdir/.find_killed.$scanid" ]; then rm -f $tmpdir/.find_killed.$scanid echo && eout "{scan} file list 'find' operation reached maximum execution time (${scan_find_timeout}sec) and was terminated" 1 else pkill -f lmd_find_sleep >> /dev/null 2>&1 fi fi if [ ! -f "$find_results" ] || [ ! -s "$find_results" ]; then if [ -z "$hscan" ]; then if [ "$days" == "all" ]; then eout "{scan} scan returned empty file list; check that path exists and contains files in scope of configuration." 1 rm -f $find_results $scan_session $runtime_ndb $runtime_hdb $runtime_hexstrings $clamscan_results $tmpdir/.tmpf* exit 0 else eout "{scan} scan returned empty file list; check that path exists, contains files in days range or files in scope of configuration." 1 rm -f $find_results $scan_session $runtime_ndb $runtime_hdb $runtime_hexstrings $clamscan_results exit 0 fi fi fi res_col="1" move_to_col="echo -en \\033[${res_col}G" tot_files=`$wc -l $find_results | awk '{print$1}'` if [ -z "$hscan" ] && [ -z "$single_filescan" ]; then if [ "$file_list" ]; then eout "{scan} user supplied file list '$file_list', found $tot_files files..." 1 else eout "{scan} file list completed in ${file_list_et}s, found $tot_files files..." 1 fi fi touch $sessdir/clean.$$ if [ ! -f "$scan_session" ]; then touch $scan_session fi if [ ! -z "$hscan" ]; then eout "{scan.hook} scan of $spath in progress (id: $datestamp.$$)" fi cnt=0 if [ -z "$mail" ] && [ -z "$sendmail" ]; then eout "{scan} no \$mail or \$sendmail binaries found, e-mail alerts disabled." fi if [ -f "$clamscan" ] && [ "$scan_clamscan" == "1" ]; then if [ -z "$hscan" ]; then eout "{scan} found clamav binary at $clamscan, using clamav scanner engine..." 1 fi if [ "$string_length_scan" == "1" ]; then if [ -z "$hscan" ]; then eout "{scan} preprocessing file list for string length hits..." 1 scan_strlen list "$find_results" >> /dev/null 2>&1 fi fi if [ -z "$hscan" ]; then eout "{scan} scan of $hrspath ($tot_files files) in progress..." 1 fi echo "$(date +"%b %d %Y %H:%M:%S") $(hostname -s) clamscan start" >> $clamscan_log clamscan_results="$tmpdir/.clamscan.$$" echo "$(date +"%b %d %Y %H:%M:%S") $(hostname -s) executed: $nice_command $clamscan $clamopts --infected --no-summary -f $find_results" >> $clamscan_log if [ "$scan_clamd_remote" == "1" ] && [ -f "$remote_clamd_config" ]; then try=0 while [ $try -le $remote_clamd_max_retry ]; do $nice_command $clamscan $clamopts --infected --no-summary -f $find_results > $clamscan_results 2>> $clamscan_log clamscan_return=$? if [ "$clamscan_return" == "2" ]; then ((try++)) echo "$(date +"%b %d %H:%M:%S") $(hostname -s) remote clamd error - retrying in $retry_sleep seconds ($try)" sleep $remote_clamd_retry_sleep else break fi done else $nice_command $clamscan $clamopts --infected --no-summary -f $find_results > $clamscan_results 2>> $clamscan_log clamscan_return=$? fi if [ "$clamscan_return" == "2" ]; then if [ "$quarantine_on_error" == "0" ] || [ -z "$quarantine_on_error" ]; then quarantine_hits=0 eout "{scan} clamscan returned an error, check $clamscan_log for details; quarantine_on_error=0 or unset, quarantine has been disabled!" 1 else eout "{scan} clamscan returned an error, check $clamscan_log for details!" 1 fi fi clamscan_fatal_error=`grep -m1 'no reply from clamd' $clamscan_results` if [ "$clamscan_fatal_error" ]; then quarantine_hits=0 eout "{scan} clamscan returned a fatal error in scan results, check $clamscan_log for details; quarantine has been disabled!" 1 fi echo "$(date +"%b %d %Y %H:%M:%S") $(hostname -s) clamscan end return $clamscan_return" >> $clamscan_log lbreakifs set for hit in `grep -E -v 'ERROR$|lstat()|no reply from clamd' $clamscan_results | sed -e 's/.UNOFFICIAL//' -e 's/ FOUND$//' | awk -F':' '{print$2":"$1}' | sed 's/.//'`; do file=`echo "$hit" | cut -d':' -f2` signame=`echo "$hit" | cut -d':' -f1` if [ ! -z "$(echo $signame | grep -E 'YARA')" ]; then signame=`echo "$signame" | sed 's/YARA\./{YARA}/'` elif [ -z "$(echo $signame | grep -E 'HEX|MD5')" ]; then signame="{CAV}$signame" fi ignore_hit=`echo $signame | grep -E -vf $ignore_sigs` if [ ! -z "$ignore_hit" ]; then record_hit "$file" "$signame" quarantine "$file" "$signame" if [ ! "$set_background" == "1" ]; then tot_hits=`$wc -l $scan_session | awk '{print$1}'` tot_cl=`$wc -l $sessdir/clean.$$ | awk '{print$1}'` if [ -z "$hscan" ]; then echo -en "\\033[${res_col}G" && echo -n "maldet($$): {scan} processing scan results for hits: $tot_hits hits $tot_cl cleaned" fi cnt="$tot_files" fi fi unset ignore_hit done lbreakifs unset echo "$(date +"%b %d %Y %H:%M:%S") $(hostname -s) clamscan end" >> $clamscan_log else if [ -z "$hscan" ]; then eout "{scan} scan of $hrspath ($tot_files files) in progress..." 1 fi lbreakifs set if [ ! -f "$scan_session" ]; then touch $scan_session fi while read rpath; do ((cnt++)) if [ -z "$hscan" ] && [ ! "$set_background" == "1" ] && [ -z "$single_filescan" ]; then tot_hit=`$wc -l $scan_session | awk '{print$1}'` cl_hit=`$wc -l $sessdir/clean.$$ | awk '{print$1}'` echo -en "\\033[${res_col}G" && echo -n "maldet($$): {scan} $cnt/$tot_files files scanned: $tot_hit hits $cl_hit cleaned" fi if [ -f "$rpath" ]; then scan_stage1 "$rpath" >> /dev/null 2>&1 fi done < $find_results lbreakifs unset fi if [ -z "$hscan" ]; then if [ ! "$set_background" == "1" ] && [ "$scan_clamscan" == "0" ] && [ -z "$single_filescan" ]; then echo fi fi scan_end_hr=`date +"%b %e %Y %H:%M:%S %z"` scan_end=`date +"%s"` scan_et=$[scan_end-scan_start] scan_et_nofl=$[scan_et-file_list_et] tot_hits=`$wc -l $scan_session | awk '{print$1}'` tot_cl=`$wc -l $sessdir/clean.$$ | awk '{print$1}'` gen_report if [ ! -z "$hscan" ]; then if [ ! "$tot_hits" == "0" ]; then echo "0 maldet: $hitname $spath" eout "{scan.hook} results returned FAIL hit found on $spath (id: $datestamp.$$)" else echo "1 maldet: OK" eout "{scan.hook} results returned OK on $spath (id: $datestamp.$$)" fi else if [ -z "$hscan" ]; then echo fi eout "{scan} scan completed on $hrspath: files $tot_files, malware hits $tot_hits, cleaned hits $tot_cl, time ${scan_et}s" 1 eout "{scan} scan report saved, to view run: maldet --report $datestamp.$$" 1 if [ "$quarantine_hits" == "0" ] && [ ! "$tot_hits" == "0" ]; then eout "{scan} quarantine is disabled! set quarantine_hits=1 in $cnffile or to quarantine results run: maldet -q $datestamp.$$" 1 fi fi if [ ! "$tot_hits" == "0" ]; then if [ "$email_ignore_clean" == "1" ] && [ ! "$tot_hits" == "$tot_cl" ]; then genalert file $nsess elif [ "$email_ignore_clean" == "0" ]; then genalert file $nsess fi if [ "$email_panel_user_alerts" == "1" ]; then genalert panel $nsess fi fi mv $scan_session $nsess_hits rm -f $find_results $scan_session $runtime_ndb $runtime_hdb $runtime_hexstrings $clamscan_results } scan_strlen() { type="$1" file="$2" if [ "$string_length_scan" == "1" ] && [ "$type" == "file" ]; then flen=`$wc -L $file 2> /dev/null | awk '{print$1}'` if [ "$flen" -ge "$string_length" ]; then eout "{strlen} malware string length hit on $file" quarantine "$file" "{SA}stat.strlength" fi elif [ "$string_length_scan" == "1" ] && [ "$type" == "list" ]; then list="$tmpdir/.strlen.flist.$$" cp $file $list sed -i -e "s/'/\\\\'/g" $list cat $list | xargs wc -L 2> /dev/null | grep -vw total >> $list.strlen awk "{if (\$1>=$string_length) print\$2}" $list.strlen >> $list.hits for i in `cat $list.hits`; do if [ -f "$i" ]; then eout "{strlen} malware string length hit on $i" quarantine "$i" "{SA}stat.strlength" fi done rm -f $list* fi } scan_stage1() { file="$1" clean_check="$2" hash=`eval $md5sum \"$file\" | awk '{print$1}'` if [ -z "$runtime_hexstrings" ]; then sigignore gensigs fi if [ ! -z "$hash" ]; then val_hash=`grep -m1 $hash $sig_user_md5_file $sig_md5_file` if [ ! -z "$val_hash" ]; then md5_hit="$hash" md5_hitname=`echo $val_hash | cut -d':' -f4` md5_hash="$hash" if [ "$clean_check" == "1" ]; then clean_failed=1 else record_hit "$file" "$md5_hitname" quarantine "$file" "$md5_hitname" fi unset val_hash md5_hit md5_hitname md5_hash else if [ -f "$file" ]; then scan_stage2 "$file" $clean_check >> /dev/null 2>&1 fi if [ -f "$file" ]; then scan_strlen file "$file" >> /dev/null 2>&1 fi fi else eout "{scan} error could not read or hash $file, do we have permission?" fi } scan_stage2() { file="$1" clean_check="$2" if [ -z "$ftype" ]; then if [ -p "$hex_fifo_path" ] && [ "$scan_hexfifo" == "1" ]; then if [ "$OSTYPE" == "FreeBSD" ]; then $od -v -N$scan_hexfifo_depth -tx1 "$file" | cut -c12-256 | tr -d ' \n' > $hex_fifo_path 2>&1 & else $od -v -w64 -N$scan_hexfifo_depth -tx1 "$file" | cut -c9-256 | tr -d '\n ' > $hex_fifo_path 2>&1 & fi val_hex=`$perl $hex_fifo_script $runtime_hexstrings` else if [ "$OSTYPE" == "FreeBSD" ]; then val_hex=`$perl $runtime_hexstrings $hex_string_script $($od -v -N$scan_hexdepth -tx1 "$file" | cut -c12-256 | tr -d ' \n')` else val_hex=`$perl $runtime_hexstrings $hex_string_script $($od -v -w$scan_hexdepth -N$scan_hexdepth -tx1 "$file" | tr -d '\n ')` fi fi if [ ! -z "$val_hex" ]; then hex_hit=`echo $val_hex | awk '{print$1}'` hex_hitname=`echo $val_hex | awk '{print$2}'` if [ "$clean_check" == "1" ]; then clean_failed=1 else record_hit "$file" "$hex_hitname" quarantine "$file" "$hex_hitname" fi unset val_hex hex_hit hex_hitname fi fi } gen_report() { if [ -f "$scan_session" ]; then tot_hits=`$wc -l $scan_session | awk '{print$1}'` nsess_hits="$sessdir/session.hits.$datestamp.$$" echo "$datestamp.$$" > $sessdir/session.last nsess="$sessdir/session.$datestamp.$$" tmpf="$nsess" . $email_template fi } trim_log() { log="$1" logtrim="$2" if [ -f "$log" ]; then log_size=`$wc -l $log | awk '{print$1}'` if [ "$log_size" -gt "$logtrim" 2> /dev/null ]; then trim=$[logtrim/10] printf "%s\n" "$trim,${log_size}d" w | ed -s $log 2> /dev/null fi elif [ ! -f "$log" ] && [ "$3" == "1" ]; then touch $log ; chmod 640 $log fi } genalert() { type="$1" file="$2" if [ "$email_alert" == "1" ] || [ "$type" == "digest" ] || [ "$type" == "daily" ]; then if [ "$type" == "file" ] && [ -f "$file" ]; then if [ -f "$mail" ]; then cat $file | $mail -s "$email_subj" $email_addr elif [ -f "$sendmail" ]; then if ! grep -q "SUBJECT: " "$file"; then echo -e "SUBJECT: $email_subj\n$(cat $file)" > $file fi cat $file | $sendmail -t $email_addr else eout "{scan} no \$mail or \$sendmail binaries found, e-mail alerts disabled." fi if [ ! "$(whoami)" == "root" ] && [ -z "$(echo $2 | grep '\@')" ]; then if [ -z "$hscan" ]; then eout "{alert} sent scan report to config default $email_addr" 1 eout "{alert} send scan report to an alternate address with: maldet --report $datestamp.$$ you@domain.com" 1 else eout "{alert} sent scan report to config default $email_addr" fi else if [ -z "$hscan" ]; then eout "{alert} sent scan report to $email_addr" 1 fi fi elif [ "$type" == "panel" ] && [ -f "$file" ]; then eout "{panel} Detecting control panel and sending alerts..." 1 control_panel="" detect_control_panel if [ "$control_panel" == "error" ] || [ "$control_panel" == "unknown" ]; then eout "{panel} Failed to set control panel. Will not send alerts to control panel account contacts." 1 else # Sort malware hits from $file and map the detected files to their system user owner file_hits=$(awk '/FILE HIT LIST:/{flag=1;next}/^=======/{flag=0}flag{print $3}' $file) for hit in $file_hits; do hit_line=$(grep "$hit" $file) if [ -f "$hit" ]; then file_owner=$(stat -c "%U" $hit) elif ! [ -f "$hit" ] && [ "$quarantine_hits" == "1" ] && [[ "$hit_line" == *"=>"* ]]; then quarantined_file=$(echo $hit_line | awk '{print $NF}') file_owner=$(awk -F':' '/^[^#]/{print $1}' ${quarantined_file}.info) fi echo "$file_owner : $hit" >> $tmpdir/.panel_alert.hits done # Sort cleaned files too if [ "$quarantine_clean" == "1" ]; then for clean_file in $(cat $sessdir/clean.$$); do if [ -f $clean_file ]; then clean_owner=$(stat -c "%U" $clean_file) fi echo "$clean_owner : $clean_file" >> $tmpdir/.panel_alert.clean done fi # Determine control panel, noop if error or none detected eout "{panel} Detected control panel $control_panel. Will send alerts to control panel account contacts." 1 user_list=$(awk '{print $1}' $tmpdir/.panel_alert.hits | sort | uniq) if [ -n "$user_list" ]; then for sys_user in $user_list; do contact_emails="" get_panel_contacts $control_panel $sys_user grep "^$sys_user " $tmpdir/.panel_alert.hits | awk '{print $3}' > $tmpdir/.${sys_user}.hits user_tot_hits=$($wc -l $tmpdir/.${sys_user}.hits | awk '{print$1}') if [ -f $tmpdir/.panel_alert.clean ]; then grep "^$sys_user " $tmpdir/.panel_alert.clean | awk '{print $3}' > $tmpdir/.${sys_user}.clean user_tot_cl=$($wc -l $tmpdir/.${sys_user}.clean | awk '{print$1}') fi tmpf=$tmpdir/.${sys_user}.alert if [ -f "$sendmail" ]; then echo "TO: $contact_emails" > $tmpf echo "FROM: $email_panel_from" >> $tmpf echo "REPLY-TO: $email_panel_replyto" >> $tmpf echo -e "SUBJECT: $email_panel_alert_subj\n" >> $tmpf . $email_panel_alert_etpl cat $tmpf | $sendmail -t elif [ -f "$mail" ] && [ "$control_panel" == "cpanel" ]; then . $email_panel_alert_etpl cat $tmpf | $mail -r $email_panel_from -s $email_panel_alert_subj $contact_emails else eout "{panel} No compatible \$sendmail or \$mail binaries found, control panel account alerts disabled." fi done fi rm -f $tmpdir/.panel_alert.hits $tmpdir/.panel_alert.clean $tmpdir/.${sys_user}.hits $tmpdir/.${sys_user}.clean $tmpf fi elif [ "$type" == "daily" ] || [ "$type" == "digest" ]; then inotify_start_time=`ps -p $(ps -A -o 'pid cmd' | grep -E maldetect | grep -E inotifywait | awk '{print$1}' | head -n1) -o lstart= 2> /dev/null` scan_start_hr=`date -d "$inotify_start_time" +"%b %e %Y %H:%M:%S %z"` scan_start_elapsed=$(($(date +'%s')-$(date -d "$scan_start_hr" +'%s'))) inotify_run_time=`echo $(($scan_start_elapsed/86400))d:$(($(($scan_start_elapsed - $scan_start_elapsed/86400*86400))/3600))h:$(($(($scan_start_elapsed - $scan_start_elapsed/86400*86400))%3600/60))m:$(($(($scan_start_elapsed - $scan_start_elapsed/86400*86400))%60))s` rm -f $tmpdir/.digest.alert.hits $tmpdir/.digest.clean.hits $tmpdir/.digest.monitor.alert $tmpdir/.digest.susp.hits scanid="$datestamp.$$" scan_session=`cat $sessdir/session.monitor.current` $tlog $scan_session digest.alert > $tmpdir/.digest.alert.hits $tlog $clean_history digest.clean.alert > $tmpdir/.digest.clean.hits $tlog $monitor_scanned_history digest.monitor.alert > $tmpdir/.digest.monitor.alert $tlog $suspend_history digest.susp.alert > $tmpdir/.digest.susp.hits tot_hits=`$wc -l $tmpdir/.digest.alert.hits | awk '{print$1}'` tot_cl=`$wc -l $tmpdir/.digest.clean.hits | awk '{print$1}'` tot_files=`$wc -l $tmpdir/.digest.monitor.alert | awk '{print$1}'` tot_susp=`$wc -l $tmpdir/.digest.susp.hits | awk '{print$1}'` trim_log $monitor_scanned_history 50000 trim_log $clean_history 50000 trim_log $suspend_history 50000 $tlog $sessdir/session.hits.$datestamp.$$ digest.alert >> /dev/null 2>&1 $tlog $clean_history digest.clean.alert >> /dev/null 2>&1 $tlog $monitor_scanned_history digest.monitor.alert >> /dev/null 2>&1 $tlog $suspend_history digest.susp.alert >> /dev/null 2>&1 if [ ! -z "$(cat $tmpdir/.digest.alert.hits)" ]; then tmpf="$tmpdir/.alert.$RANDOM.$$" if [ "$tot_hits" -gt "$tot_files" ]; then tot_files="$tot_hits" fi . $email_template cp $tmpf $sessdir/session.$scanid grep -E '^{.*}' $sessdir/session.$scanid > $sessdir/session.hits.$scanid echo "$scanid" > $sessdir/session.last email_subj="${email_subj}: monitor summary" if [ -f "$mail" ]; then cat $tmpf | $mail -s "$email_subj" $email_addr eout "{alert} sent $type alert to $email_addr" elif [ -f "$sendmail" ]; then if ! grep -q "SUBJECT: " "$tmpf"; then echo -e "SUBJECT: $email_subj\n$(cat $tmpf)" > $tmpf fi cat $tmpf | $sendmail -t $email_addr eout "{alert} sent $type alert to $email_addr" else eout "{scan} no \$mail or \$sendmail binaries found, e-mail alerts disabled." fi rm -f $tmpf $tmpdir/.digest.alert.hits $tmpdir/.digest.clean.hits $tmpdir/.digest.monitor.alert $tmpdir/.digest.susp.hits fi else eout "{alert} file input error, alert discarded." fi fi if [ "$slack_alert" == "1" ]; then if [ "$type" == "file" ] && [ -f "$file" ] && [ -f "$curl" ]; then slack_response=$($curl -s -F "token=$slack_token" -F "file=@$file" -F "filename=$slack_subj" -F "channels=$slack_channels" -X POST https://slack.com/api/files.upload | grep -oP '^{"ok":true') if [ "$slack_response" ]; then eout "{alert} scan report sent to slack channel(s): $slack_channels" 0 else eout "{alert} could not upload scan report to slack channel(s), alert discarded" 1 fi else eout "{alert} could not upload scan report to slack channel(s), alert discarded" 1 fi fi } monitor_kill() { touch $tmpdir/stop_monitor inotify_pid=`pgrep -f inotify.paths.[0-9]+` if [ -f "$tmpdir/monitor.pid" ]; then monitor_pid=`cat $tmpdir/monitor.pid` exit_code="0" else exit_code="1" fi monitor_pgid=`ps -p "$monitor_pid" -o pgid= | tr -d ' '` kill -9 -- $inotify_pid -$monitor_pgid >> /dev/null 2>&1 exit $exit_code } monitor_cycle() { if [ "$BASHPID" ]; then echo "$BASHPID" > $tmpdir/monitor.pid else pgrep maldet > $tmpdir/monitor.pid fi inotify_cycle_runtime=0 while [ ! -f "$tmpdir/stop_monitor" ]; do inotify_pid=`pgrep -f inotify.paths.[0-9]+` if [ -z "$inotify_pid" ]; then eout "{mon} no inotify process found, exiting (are we a zombie process?)" 1 exit fi log_size=`$wc -l $inotify_log | awk '{print$1}'` if [ "$log_size" -ge "$inotify_trim" ]; then trim=$(($log_size - 1000)) log_chars=`printf "%s\n" "1,${trim}p" | ed -s $inotify_log 2> /dev/null | wc -c` tlog_new=$(( `cat $inspath/tmp/inotify` - $log_chars )) echo $tlog_new > $inspath/tmp/inotify printf "%s\n" "1,${trim}d" w | ed -s $inotify_log 2> /dev/null eout "{mon} inotify log file trimmed" fi if [ "$inotify_cycle_runtime" -ge "$inotify_reloadtime" ] || [ -f "$inspath/reload_monitor" ]; then if [ -f "$inspath/reload_monitor" ]; then rm -f $inspath/reload_monitor fi source $cnf source $intcnf import_conf inotify_cycle_runtime=0 if [ -f "$ignore_file_ext" ]; then if [ ! "$(cat $ignore_file_ext)" == "" ]; then for i in `cat $ignore_file_ext`; do if [ "$ignore_fext" == "" ]; then ignore_fext="-not -iname \"*$i\"" else ignore_fext="$ignore_fext -not -iname \"*$i\"" fi done fi fi if [ "$scan_ignore_root" == "1" ]; then ignore_root="-not -uid 0 -not -gid 0" fi if [ "$scan_ignore_user" ]; then for i in `echo $scan_ignore_user | tr ', ' ' '`; do if [ "$ignore_user" == "" ]; then ignore_user="-not -user $i" else ignore_user="$ignore_user -not -user $i" fi done fi if [ "$scan_ignore_group" ]; then for i in `echo $scan_ignore_group | tr ', ' ' '`; do if [ "$ignore_group" == "" ]; then ignore_group="-not -group $i" else ignore_group="$ignore_group -not -group $i" fi done fi eout "{mon} reloaded configuration data" 1 fi sleep $inotify_sleep inotify_cycle_runtime=$[inotify_sleep+inotify_cycle_runtime] if [ -f "$ignore_sigs" ]; then ignore_sigs_current_md5=`md5sum $ignore_sigs | awk '{print$1}'` if [ ! "$ignore_sigs_current_md5" == "$ignore_sigs_last_md5" ]; then sigignore 1 gensigs ignore_sigs_current_md5=`md5sum $ignore_sigs | awk '{print$1}'` eout "{mon} regenerated signature files on ignore_sigs file change detected" 1 fi ignore_sigs_last_md5="$ignore_sigs_current_md5" fi if [ "$scan_clamscan" == "1" ]; then monitor_mode=1 clamselector fi monitor_check done rm -f $tmpdir/stop_monitor eout "{mon} monitoring terminated by user, inotify killed." exit } cleanup_scanlist() { # Checks for files that are already gone (temporary files, cache # files, ...) and removes them from $monitor_scanlist, so we don't # get errors and too many retries TMP_FILE=$(mktemp -p /var/lib/maldetect/tmp) lbreakifs set for FILE in $(cat $monitor_scanlist); do if [ -f "$FILE" ]; then echo "$FILE" >> $TMP_FILE fi done lbreakifs unset mv $TMP_FILE $monitor_scanlist } monitor_check() { monitor_scanlist="$tmpdir/.monitor.scan.${RANDOM}${RANDOM}" touch $monitor_scanlist ; chmod 600 $monitor_scanlist $tlog $inotify_log inotify | grep -E " CREATE| MODIFY| MOVED_TO" | awk -F" CREATE| MODIFY| MOVED_TO" '{print $1}' | sort -u | grep -vf $ignore_paths> $monitor_scanlist if [ "$scan_clamscan" == "1" ]; then clamscan_results="$tmpdir/.clamscan.result.${RANDOM}${RANDOM}" touch $clamscan_results ; chmod 600 $clamscan_results $nice_command $clamscan $clamopts --infected --no-summary -f $monitor_scanlist > $clamscan_results 2>> $clamscan_log || clamscan_return=$? if [ "$scan_clamd_remote" == "1" ] && [ -f "$remote_clamd_config" ]; then try=0 while [ $try -le $remote_clamd_max_retry ]; do cleanup_scanlist $monitor_scanlist $nice_command $clamscan $clamopts --infected --no-summary -f $monitor_scanlist > $clamscan_results 2>> $clamscan_log clamscan_return=$? if [ "$clamscan_return" == "2" ]; then ((try++)) echo "$(date +"%b %d %H:%M:%S") $(hostname -s) remote clamd error - retrying in $retry_sleep seconds ($try)" >> $clamscan_log sleep $remote_clamd_retry_sleep else break fi done else $nice_command $clamscan $clamopts --infected --no-summary -f $monitor_scanlist > $clamscan_results 2>> $clamscan_log || clamscan_return=$? fi if [ "$inotify_verbose" == "1" ]; then for file in `cat $monitor_scanlist | tr ' ' '%'`; do file=`echo $file | tr '%' ' '` eout "{mon} inotify clamav file scan on $file" done fi lbreakifs set for hit in `grep -E -v 'ERROR$|lstat()' $clamscan_results | sed -e 's/.UNOFFICIAL//' -e 's/ FOUND$//' | awk -F':' '{print$2":"$1}' | sed 's/.//'`; do file=`echo "$hit" | cut -d':' -f2` signame=`echo "$hit" | cut -d':' -f1` if [ ! -z "$(echo $signame | grep -E 'YARA')" ]; then signame=`echo "$signame" | sed 's/YARA\./{YARA}/'` elif [ -z "$(echo $signame | grep -E 'HEX|MD5')" ]; then signame="{CAV}$signame" fi ignore_hit=`echo $signame | grep -E -vf $ignore_sigs` if [ -f "$file" ] && [ ! -z "$ignore_hit" ]; then record_hit "$file" "$signame" quarantine "$file" "$signame" fi unset ignore_hit done lbreakifs unset scanned_count=`wc -l $monitor_scanlist | awk '{print$1}'` eout "{mon} scanned ${scanned_count} new/changed files with clamav engine" rm -f $clamscan_results $monitor_scanlist else for file in `cat $monitor_scanlist | tr ' ' '%'`; do file=`echo $file | tr '%' ' '` if [ -f "$file" ]; then for fscan in `$nice_command $find "$file" -maxdepth 1 $find_opts -type f -size +${scan_min_filesize}c -size -$scan_max_filesize -not -perm 000 $ignore_fext $ignore_root $ignore_user $ignore_group 2> /dev/null`; do if [ "$inotify_verbose" == "1" ]; then eout "{mon} inotify native file scan on $file" fi echo "$file" >> $monitor_scanned_history scan_stage1 "$fscan" >> /dev/null 2>&1 done fi done scanned_count=`wc -l $monitor_scanlist | awk '{print$1}'` eout "{mon} scanned ${scanned_count} new/changed files with native engine" rm -f $clamscan_results $monitor_scanlist fi } monitor_init() { inopt="$1" scan_session="$sessdir/session.hits.$datestamp.$$" touch $scan_session echo "$scan_session" > $sessdir/session.monitor.current if [ "$inopt" == "" ]; then eout "invalid usage of -m|--monitor, aborting." 1 exit fi if [ ! -f "$inotify" ]; then eout "{mon} could not find inotifywait command, install yum package inotify-tools or download from https://github.com/rvoicilas/inotify-tools/wiki/" 1 exit fi ksup=0 if [ -f "/boot/System.map-$(uname -r)" ]; then ksup=`grep inotify_ /boot/System.map-$(uname -r)` fi if [ -f "/boot/config-$(uname -r)" ]; then ksup=`grep -m1 CONFIG_INOTIFY /boot/config-$(uname -r)` fi if [ -z "$ksup" ]; then eout "{mon} kernel does not support inotify(), aborting." 1 exit fi inotify_pid=`pgrep -f inotify.paths.[0-9]+` if [ ! -z "$inotify_pid" ]; then eout "{mon} existing inotify process detected (try -k): $inotify_pid" 1 exit fi rm -f $tmpdir/stop_monitor $tmpdir/inotifywait.pid if [ -f "/proc/sys/fs/inotify/max_user_instances" ] && [ -f "/proc/sys/fs/inotify/max_user_watches" ]; then cur_user_watches=`cat /proc/sys/fs/inotify/max_user_watches` cur_user_instances=`cat /proc/sys/fs/inotify/max_user_instances` else eout "{mon} could not find fs.inotify.max_user_instances|watches tunable files, aborting." 1 exit fi users_tot=`cat /etc/passwd | grep -ic home` inotify_user_watches=$[inotify_base_watches*users_tot] if [ "$cur_user_instances" -lt "$inotify_user_instances" ]; then eout "{mon} set inotify max_user_instances to $inotify_user_instances" 1 echo $inotify_user_instances > /proc/sys/fs/inotify/max_user_instances fi if [ "$cur_user_watches" -lt "$inotify_user_watches" ]; then eout "{mon} set inotify max_user_watches to $inotify_user_watches" 1 echo $inotify_user_watches > /proc/sys/fs/inotify/max_user_watches fi icnt=0 inotify_fpaths="$sessdir/inotify.paths.$$" rm -f $inotify_fpaths touch $inotify_log chmod 640 $inotify_log if [ "$(echo $inopt | grep -iE 'user(s?)')" ]; then for i in `cat /etc/passwd | cut -d':' -f1,3,6 | sort`; do user=`echo $i | cut -d':' -f1` user_id=`echo $i | cut -d':' -f2` user_home=`echo $i | cut -d':' -f3` icnt=$[icnt+1] if [ "$user_id" -ge "$inotify_minuid" ]; then if [ ! -z "$inotify_docroot" ] && [ -d "$user_home" ]; then lbreakifs set for docroot in `echo $inotify_docroot | tr ', ' '\n'`; do if [ -d "$user_home/$docroot" ]; then echo "$user_home/$docroot" >> $inotify_fpaths eout "{mon} added $user_home/$docroot to inotify monitoring array" 1 fi done lbreakifs unset elif [ -d "$user_home" ]; then echo "$user_home" >> $inotify_fpaths eout "{mon} added $user_home to inotify monitoring array" 1 else eout "{mon} could not find any suitable user home paths" fi fi done if [ -d "/dev/shm" ]; then echo "/dev/shm" >> $inotify_fpaths eout "{mon} added /dev/shm to inotify monitoring array" 1 fi if [ -d "/var/tmp" ]; then echo "/var/tmp" >> $inotify_fpaths eout "{mon} added /var/tmp to inotify monitoring array" 1 fi if [ -d "/tmp" ]; then echo "/tmp" >> $inotify_fpaths eout "{mon} added /tmp to inotify monitoring array" 1 fi elif [ -f "$inopt" ]; then tot_paths=`$wc -l $inopt | awk '{print$1}'` if [ "$tot_paths" == "0" ]; then eout "{mon} no paths specified in $inopt, aborting." 1 exit fi for i in `cat $inopt`; do if [ -d "$i" ]; then eout "{mon} added $i to inotify monitoring array" 1 echo "$i" >> $inotify_fpaths else eout "{mon} ignored invalid path $i" 1 fi done elif [ -d "$inopt" ] || [ "$(echo $inopt | grep -E ".*,.*")" ]; then for i in `echo $inopt | tr ',' '\n'`; do if [ -d "$i" ]; then eout "{mon} added $i to inotify monitoring array" 1 echo "$i" >> $inotify_fpaths else eout "{mon} invalid path $i specified, ignoring." 1 fi done else eout "{mon} no valid option or invalid file/path provided, aborting." 1 exit fi if [ -f "$ignore_inotify" ] && [ -s "$ignore_inotify" ]; then for igfile in `cat $ignore_inotify | grep -vE '^$'`; do if [ "$igregexp" ]; then igregexp="$igregexp|$igfile" else igregexp="($igfile" fi done if [ "$igregexp" ]; then igregexp="$igregexp)" exclude="--exclude $igregexp" fi fi tot_paths=`$wc -l $inotify_fpaths | awk '{print$1}'` eout "{mon} starting inotify process on $tot_paths paths, this might take awhile..." 1 if [ ! "$inotify_cpunice" ]; then inotify_cpunice=19 fi if [ ! "$inotify_ionice" ]; then inotify_ionice=6 fi if [ -f "$nice" ]; then nice_command="$nice -n $inotify_cpunice" fi if [ -f "$ionice" ]; then nice_command="$nice_command $ionice -c2 -n $inotify_ionice" fi if [ -f "$cpulimit" ] && [ "$inotify_cpulimit" -gt 2> /dev/null "0" ]; then max_cpulimit=$[$(grep -E -w processor /proc/cpuinfo -c)*100] if [ "$inotify_cpulimit" -gt "$max_cpulimit" ]; then scan_cpulimit="0" else nice_command="$cpulimit -l $scan_cpulimit -- $nice_command" fi fi $nice_command $inotify -r --fromfile $inotify_fpaths $exclude --timefmt "%d %b %H:%M:%S" --format "%w%f %e %T" -m -e create,move,modify >> $inotify_log 2>&1 & sleep 2 inotify_pid=`pgrep -f inotify.paths.[0-9]+` if [ -z "$inotify_pid" ]; then eout "{mon} no inotify process found, check $inotify_log for errors." 1 exit else eout "{mon} inotify startup successful (pid: $inotify_pid)" 1 eout "{mon} inotify monitoring log: $inotify_log" 1 echo "$inotify_pid" > $tmpdir/inotifywait.pid fi monitor_cycle >> /dev/null 2>&1 & } checkout() { file="$1" host=ftp.rfxn.com user=anonymous@rfxn.com passwd=anonymous@rfxn.com upath=incoming cfile="$startdir/$file" if [ -f "$cfile" ]; then file="$cfile" fi if [ -f "$file" ]; then filename=`basename "$file" | tr -d '[:cntrl:]' | tr -d '[:space:]'` if [ -z "$filename" ]; then storename="$storename_prefix" else storename="$storename_prefix.$filename" fi eout "{checkout} uploading $file to $host" 1 (ftp -v -n -p -i $host || ftp -v -n -i $host) << EOT user $user@rfxn.com $passwd prompt cd $upath lcd $lcd binary put "$file" "$storename.bin" ascii put "$file" "$storename.ascii" bye EOT elif [ -d "$file" ]; then tmpf="$tmpdir/.co$$" find $file -type f > $tmpf cofiles=`wc -l $tmpf | awk '{print$1}'` if [ "$cofiles" -ge "25" ]; then eout "{checkout} path $file contains $cofiles, limit of 50 file uploads, aborting!" 1 rm -f $tmpf fi for i in `cat $tmpf`; do filename=`basename "$i" | tr -d '[:cntrl:]' | tr -d '[:space:]'` if [ -z "$filename" ]; then storename="$storename_prefix" else storename="$storename_prefix.$filename" fi (ftp -v -n -p -i $host || ftp -v -n -i $host) << EOT user $user $passwd prompt cd $upath lcd $lcd binary put "$i" "$storename.bin" ascii put "$i" "$storename.ascii" bye EOT done fi } gensigs() { runtime_ndb="$tmpdir/.runtime.user.$$.ndb" runtime_hdb="$tmpdir/.runtime.user.$$.hdb" runtime_hexstrings="$tmpdir/.runtime.hexsigs.$$" rm -f $runtime_ndb $runtime_hdb $runtime_hexstrings 2> /dev/null ln -fs $runtime_ndb $sigdir/lmd.user.ndb 2> /dev/null ln -fs $runtime_hdb $sigdir/lmd.user.hdb 2> /dev/null if [ -s "$sig_user_hex_file" ]; then cat "$sig_hex_file" "$sig_user_hex_file" | grep -vE '^\s*$' > $runtime_hexstrings else cat "$sig_hex_file" > $runtime_hexstrings fi for cp in $clamav_paths; do clamav_linksigs "$cp" done if [ "$scan_clamscan" == "1" ]; then if [ -s "$sig_user_hex_file" ]; then for i in `cat $sig_user_hex_file | sed 's/{HEX}//' | tr ':' '%' | grep -vE "^\s*$"`; do name=`echo $i | tr '%' ' ' | awk '{print$2}'` hex=`echo $i | tr '%' ' ' | awk '{print$1}'` if [ ! -z "$name" ] && [ ! -z "$hex" ]; then echo "{HEX}$name:0:*:$hex" >> $runtime_ndb fi done cat $sig_cav_hex_file >> $runtime_ndb else cp $sig_cav_hex_file $runtime_ndb fi if [ -s "$sig_user_md5_file" ]; then cat "$sig_user_md5_file" "$sig_md5_file" | grep -vE "^\s*$" | sort -u > $runtime_hdb else cp "$sig_cav_md5_file" "$runtime_hdb" fi fi } sigignore() { sil="$1" chk=`$wc -l $ignore_sigs | awk '{print$1}'` if [ ! "$chk" == "0" ]; then cat $sig_hex_file | grep -E -vf $ignore_sigs > $sig_hex_file.new mv $sig_hex_file.new $sig_hex_file cat $sig_md5_file | grep -E -vf $ignore_sigs > $sig_md5_file.new mv $sig_md5_file.new $sig_md5_file chmod 640 $sig_md5_file $sig_hex_file if [ "$sil" == "1" ] || [ "$hscan" == "1" ]; then eout "{glob} processed $chk signature ignore entries" else eout "{glob} processed $chk signature ignore entries" 1 fi fi } lmdup() { tmpwd="$tmpdir/.lmdup.$RANDOM.$$" upstreamver="$tmpwd/.lmdup_vercheck.$$" mkdir -p $tmpwd ; chmod 700 $tmpwd if [ "$lmdup_beta" ]; then lmd_hash_url="${lmd_hash_url}.beta" lmd_version_url="${lmd_version_url}.beta" lmd_current_tgzfile="maldetect-beta.tar.gz" fi eout "{update} checking for available updates..." 1 get_remote_file "$lmd_version_url" "update" "1" upstreamver="$return_file" if [ -s "$upstreamver" ]; then installedver=`echo $ver | tr -d '.'` if [ "$(echo $installedver | wc -L)" -eq "2" ]; then installedver="${installedver}0" fi upstreamver_readable=`cat $upstreamver` upstreamver=`cat $upstreamver | tr -d '.'` if [ "$(echo $upstreamver | wc -L)" -eq "2" ]; then upstreamver="${upstreamver}0" fi if [ "$upstreamver" -gt "$installedver" ]; then eout "{update} new version $upstreamver_readable found, updating..." 1 doupdate=1 elif [ "$lmdup_force" ]; then eout "{update} version update with --force requested, updating..." 1 doupdate=1 elif [ "$autoupdate_version_hashed" == "1" ]; then eout "{update} hashing install files and checking against server..." 1 eval $md5sum $inspath/maldet $intfunc | awk '{print$1}' | tr '\n' ' ' | tr -d ' ' > $lmd_hash_file upstreamhash="$tmpwd/.lmdup_hashcheck$$" get_remote_file "$lmd_hash_url" "update" "1" upstreamhash="$return_file" if [ -s "$upstreamhash" ]; then installed_hash=`cat $lmd_hash_file` current_hash=`cat $upstreamhash` if [ ! "$installed_hash" == "$current_hash" ]; then eout "{update} version check shows latest but hash check failed, forcing update..." 1 doupdate=1 else eout "{update} latest version already installed." 1 fi else eout "{update} could not download upstream hash file ($lmd_hash_url), please try again later." 1 cd $inspath ; rm -rf $tmpwd clean_exit exit 1 fi else eout "{update} no updates available, latest version already installed." 1 fi else eout "{update} could not download version file from server, please try again later." 1 cd $inspath ; rm -rf $tmpwd clean_exit exit 1 fi if [ "$doupdate" ]; then cd $tmpwd/ get_remote_file "${lmd_current_tgzbase_url}/${lmd_current_tgzfile}" "update" "1" "$tmpwd/${lmd_current_tgzfile}" get_remote_file "${lmd_current_tgzbase_url}/${lmd_current_tgzfile}.md5" "update" "1" "$tmpwd/${lmd_current_tgzfile}.md5" if [ -s "$tmpwd/${lmd_current_tgzfile}.md5" ] && [ -s "$tmpwd/${lmd_current_tgzfile}" ]; then upstream_md5=`cat $tmpwd/${lmd_current_tgzfile}.md5 | awk '{print$1}'` local_md5=`eval $md5sum $tmpwd/${lmd_current_tgzfile} | awk '{print$1}'` if [ ! "$upstream_md5" == "$local_md5" ]; then eout "{update} unable to verify md5sum of ${lmd_current_tgzfile}, update failed!" 1 cd $inspath ; rm -rf $tmpwd clean_exit exit 1 else eout "{update} verified md5sum of ${lmd_current_tgzfile}" 1 fi else eout "{update} could not download ${lmd_current_tgzfile} or .md5, please try again later." 1 cd $inspath ; rm -rf $tmpwd clean_exit exit 1 fi if [ -s "$tmpwd/${lmd_current_tgzfile}" ]; then tar xfz ${lmd_current_tgzfile} rm -f ${lmd_current_tgzfile} ${lmd_current_tgzfile}.md5 cd maldetect-${upstreamver_readable} chmod 750 install.sh sh -c './install.sh' >> /dev/null 2>&1 cp -f $inspath.last/sigs/custom.* $sigdir/ 2> /dev/null cp -f $inspath.last/clean/custom.* $inspath/clean/ 2> /dev/null eout "{update} completed update v$ver ${installed_hash:0:6} => v$upstreamver_readable ${upstream_md5:0:6}, running signature updates..." 1 $inspath/maldet --update 1 eout "{update} update and config import completed" 1 else eout "{update} could not download ${lmd_current_tgzfile}, please try again later." 1 cd $inspath ; rm -rf $tmpwd clean_exit exit 1 fi fi cd $inspath ; rm -rf $tmpwd } sigup() { eout "{sigup} performing signature update check..." 1 tmpwd="$tmpdir/.sigup.$RANDOM.$$" mkdir -p $tmpwd ; chmod 700 $tmpwd import_user_sigs if [ -z "$sig_version" ]; then eout "{sigup} could not determine signature version" 1 sig_version=0 else eout "{sigup} local signature set is version $sig_version" 1 fi get_remote_file "$sig_version_url" "sigup" "1" upstream_sigver="$return_file" if [ ! -f "$upstream_sigver" ] || [ ! -s "$upstream_sigver" ]; then eout "{sigup} could not download signature data from server, please try again later." 1 clean_exit exit 1 else nver=`cat $upstream_sigver` fi if [ -f "$sig_md5_file" ]; then lines_md5=`$wc -l $sig_md5_file | awk '{print$1}'` else lines_md5=0 fi if [ -f "$sig_hex_file" ]; then lines_hex=`$wc -l $sig_hex_file | awk '{print$1}'` else lines_hex="0" fi if [ ! -f "$sig_md5_file" ] || [ ! -f "$sig_hex_file" ]; then sig_version=2012010100000 eout "{sigup} signature files missing or corrupted, forcing update..." 1 elif [ "$lines_md5" -lt "1000" ] || [ "$lines_hex" -lt "1000" ]; then sig_version=2012010100000 eout "{sigup} signature files corrupted, forcing update..." 1 elif [ "$sigup_force" ]; then sig_version=2012010100000 eout "{sigup} signature update with --force requested, forcing update..." 1 fi if [ "$nver" != "$sig_version" ]; then cd $tmpwd/ tar=`which tar 2> /dev/null` eout "{sigup} new signature set $nver available" 1 eout "{sigup} downloading $sig_sigpack_url" 1 get_remote_file "$sig_sigpack_url" "sigup" "1" "$tmpwd/maldet-sigpack.tgz" get_remote_file "${sig_sigpack_url}.md5" "sigup" "1" "$tmpwd/maldet-sigpack.tgz.md5" eout "{sigup} downloading $sig_clpack_url" 1 get_remote_file "$sig_clpack_url" "sigup" "1" "$tmpwd/maldet-clean.tgz" get_remote_file "${sig_clpack_url}.md5" "sigup" "1" "$tmpwd/maldet-clean.tgz.md5" if [ -f "$tmpwd/maldet-sigpack.tgz.md5" ]; then sigpack_md5=`eval $md5sum maldet-sigpack.tgz | awk '{print$1}'` sigpack_goodmd5=`cat maldet-sigpack.tgz.md5 | awk '{print$1}'` if [ ! "$sigpack_md5" == "$sigpack_goodmd5" ]; then eout "{sigup} unable to verify md5sum of maldet-sigpack.tgz, please try again or contact proj@rfxn.com" 1 sigpackfail=1 else eout "{sigup} verified md5sum of maldet-sigpack.tgz" 1 if [ -f "$tmpwd/maldet-sigpack.tgz" ] && [ -s "$tmpwd/maldet-sigpack.tgz" ]; then tar xfz $tmpwd/maldet-sigpack.tgz 2> /dev/null if [ -d "$tmpwd/sigs" ]; then mkdir -p $sigdir.old 2> /dev/null rm -f $sigdir.old/* 2> /dev/null cp -f $sigdir/* $sigdir.old/ 2> /dev/null cp -f $tmpwd/sigs/* $sigdir 2> /dev/null eout "{sigup} unpacked and installed maldet-sigpack.tgz" 1 for cp in $clamav_paths; do clamav_linksigs "$cp" done killall -SIGUSR2 clamd 2> /dev/null else eout "{sigup} something went wrong unpacking $sig_sigpack_url, aborting!" 1 sigpackfail=1 fi else eout "{sigup} could not download $sig_sigpack_url" 1 sigpackfail=1 fi fi else eout "{sigup} could not download ${sig_sigpack_url}.md5" 1 sigpackfail=1 fi if [ -f "$tmpwd/maldet-clean.tgz.md5" ]; then clpack_md5=`eval $md5sum maldet-clean.tgz | awk '{print$1}'` clpack_goodmd5=`cat maldet-clean.tgz.md5 | awk '{print$1}'` if [ ! "$clpack_md5" == "$clpack_goodmd5" ]; then eout "{sigup} unable to verify md5sum of maldet-clean.tgz, please try again or contact proj@rfxn.com" 1 clpackfail=1 else eout "{sigup} verified md5sum of maldet-clean.tgz" 1 if [ -f "$tmpwd/maldet-clean.tgz" ] && [ -s "$tmpwd/maldet-clean.tgz" ]; then tar xfz $tmpwd/maldet-clean.tgz cp -f $tmpwd/clean/* $cldir eout "{sigup} unpacked and installed maldet-clean.tgz" 1 else eout "{sigup} error handling $sig_clpack_url, file is either missing or zero sized, aborting!" 1 clpackfail=1 fi fi else eout "{sigup} could not download ${sig_sigpack_url}.md5" 1 clpackfail=1 fi if [ "$sigpackfail" ]; then cd $inspath rm -rf $tmpwd clean_exit exit 1 else eout "{sigup} signature set update completed" 1 sigignore hex_sigs=`$wc -l $sig_hex_file | awk '{print$1}'` md5_sigs=`$wc -l $sig_md5_file | awk '{print$1}'` yara_sigs=`grep -E -c ^rule $sig_yara_file | awk '{print$1}'` if [ ! -f "$sig_user_md5_file" ]; then user_hex_sigs=0 else user_hex_sigs=`$wc -l $sig_user_hex_file | awk '{print$1}'` fi if [ ! -f "$sig_user_hex_file" ]; then user_md5_sigs=0 else user_md5_sigs=`$wc -l $sig_user_md5_file | awk '{print$1}'` fi user_sigs=$[user_hex_sigs+user_md5_sigs] tot_sigs=$[md5_sigs+hex_sigs+user_hex_sigs+user_md5_sigs+yara_sigs] eout "{sigup} $tot_sigs signatures ($md5_sigs MD5 | $hex_sigs HEX | $yara_sigs YARA | $user_sigs USER)" 1 fi cd $inspath rm -rf $tmpwd else eout "{sigup} latest signature set already installed" 1 cd $inspath rm -rf $tmpwd fi } postrun() { rm -f $find_results $scan_session $runtime_ndb $runtime_hdb $runtime_hexstrings $clamscan_results $tmpdir/.tmpf* 2> /dev/null if [ ! "$tot_hits" ]; then exit 0 elif [ "$tot_hits" == "0" ]; then exit 0 elif [ "$tot_hits" -ge "1" ]; then exit 2 fi }
Close