|
Directory : /scripts2/ |
n/bash
###############################################################################
# CronCheck
#
# Revisa los crontabs de usuarios cPanel y genera un reporte plano con
# tareas sospechosas.
#
# El script no envía mails. Otro proceso centralizado recolecta los reportes.
###############################################################################
set -o nounset
set -o pipefail
############################
# Configuración
############################
REPORT="/etc/sitioshispanos/croncheck_report.txt"
SCORE_THRESHOLD=8 # Score que deben alcanzar los patrones menos obvios para considerarse sospechosos
MAX_COMMAND_LENGTH=250 # Largo minimo de la cadena del cron antes de considerarse sospechoso
DEBUG=0 # Por testeos
# Reglas de schedule que generan reporte directo. Se pueden agregar mas patrones en el Array
SCHEDULE_CRITICAL_PATTERNS=(
'^\*[[:space:]]+\*[[:space:]]+\*[[:space:]]+\*[[:space:]]+\*$' # Si el cron es de tipo * * * * * (1 segundo) se reporta
'^@reboot' # Si contiene @reboot probablemente sea virus.
)
# Reglas críticas sobre el command. Se pueden agregar mas patrones en el Array
CRITICAL_PATTERNS=(
'base64'
'base64_decode'
'eval[[:space:]]*\('
'php[[:space:]]+-r'
'perl[[:space:]]+-e'
'python[[:space:]]+-c'
'(curl|wget).*\|.*(sh|bash|php|perl|python)'
'/dev/shm/'
'/tmp/'
)
# Estos patrones son los que pueden o no ser virus. Se le da un valor a cada uno y no deben sumar mas de 8 por cron o se reportará como sospechoso.
# Formato: patron|puntaje
# Se pueden agregar mas patrones y puntajes en el Array. El puntaje se puede modificar
SCORE_PATTERNS=(
'http://|3'
'https://|1'
'curl|2'
'wget|2'
'openssl|2'
'xxd|2'
)
# Patrones ignorados.
# Inicialmente vacío.
IGNORE_PATTERNS=(
)
############################
# Inicialización
############################
mkdir -p "$(dirname "$REPORT")"
: > "$REPORT"
############################
# Funciones
############################
debug() {
[[ "$DEBUG" -eq 1 ]] && echo "DEBUG: $*"
}
registrar_reporte() {
local usuario="$1"
local tipo="$2"
local score="$3"
local cron="$4"
printf '%s|%s|%s|%s\n' \
"$usuario" \
"$tipo" \
"$score" \
"$cron" >> "$REPORT"
}
esta_ignorado() {
local command="$1"
for patron in "${IGNORE_PATTERNS[@]}"; do
[[ -z "$patron" ]] && continue
if [[ "$command" =~ $patron ]]; then
return 0
fi
done
return 1
}
analizar_schedule() {
local usuario="$1"
local schedule="$2"
local cron="$3"
for patron in "${SCHEDULE_CRITICAL_PATTERNS[@]}"; do
if [[ "$schedule" =~ $patron ]]; then
registrar_reporte \
"$usuario" \
"SCHEDULE" \
"-" \
"$cron"
return 0
fi
done
return 1
}
analizar_critical() {
local usuario="$1"
local command="$2"
local cron="$3"
for patron in "${CRITICAL_PATTERNS[@]}"; do
if [[ "$command" =~ $patron ]]; then
registrar_reporte \
"$usuario" \
"CRITICAL" \
"-" \
"$cron"
return 0
fi
done
return 1
}
calcular_score() {
local command="$1"
local score=0
for regla in "${SCORE_PATTERNS[@]}"; do
local patron="${regla%|*}"
local puntos="${regla##*|}"
if [[ "$command" =~ $patron ]]; then
((score+=puntos))
fi
done
if (( ${#command} > MAX_COMMAND_LENGTH )); then
((score+=2))
fi
echo "$score"
}
analizar_cron() {
local usuario="$1"
local cron="$2"
local min=""
local hour=""
local dom=""
local month=""
local dow=""
local command=""
local schedule=""
local score=""
[[ -z "$cron" ]] && return
# Ignorar comentarios.
[[ "$cron" =~ ^[[:space:]]*# ]] && return
# Ignorar variables:
# SHELL=
# MAILTO=
# PATH=
[[ "$cron" =~ ^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*= ]] && return
# Macros de cron:
# @reboot
# @daily
# etc.
if [[ "$cron" =~ ^[[:space:]]*@ ]]; then
schedule=$(echo "$cron" | awk '{print $1}')
command=$(echo "$cron" | cut -d' ' -f2-)
else
read -r min hour dom month dow command <<< "$cron"
schedule="$min $hour $dom $month $dow"
fi
[[ -z "$command" ]] && return
debug "USER=[$usuario]"
debug "SCHEDULE=[$schedule]"
debug "COMMAND=[$command]"
if esta_ignorado "$command"; then
return
fi
if analizar_critical "$usuario" "$command" "$cron"; then
return
fi
if analizar_schedule "$usuario" "$schedule" "$cron"; then
return
fi
score=$(calcular_score "$command")
if (( score >= SCORE_THRESHOLD )); then
registrar_reporte \
"$usuario" \
"SCORE" \
"$score" \
"$cron"
fi
}
procesar_usuario() {
local usuario="$1"
while IFS= read -r cron; do
analizar_cron "$usuario" "$cron"
done < <(crontab -u "$usuario" -l 2>/dev/null)
}
############################
# Procesamiento
############################
while IFS= read -r usuario; do
# Eliminar espacios iniciales y finales.
usuario="${usuario#"${usuario%%[![:space:]]*}"}"
usuario="${usuario%"${usuario##*[![:space:]]}"}"
[[ -z "$usuario" ]] && continue
procesar_usuario "$usuario"
done < <(cut -f2 -d: /etc/trueuserdomains)
exit 0