#!/usr/bin/env bash
#=============================================================================
# Sentinela ? Intelligent Linux Security Framework
# ids/scanners.sh ? Detección de escáneres y herramientas automatizadas
#=============================================================================
#
# Detecta herramientas de reconocimiento y ataque por User-Agent,
# patrones de solicitud y comportamiento.
#
# Herramientas detectadas:
# - sqlmap, nikto, nmap, masscan, zgrab
# - acunetix, burpsuite, netsparker, appscan
# - wpscan, joomscan, droopescan
# - gobuster, dirbuster, dirsearch, wfuzz, ffuf
# - hydra, medusa, thc-hydra
# - openvas, nessus, nexpose
# - whatweb, wappalyzer
# - nuclei, httpx, subfinder
# - arachni, w3af, skipfish
# - gospider, hakrawler, katana
# - Chrome headless, PhantomJS, Selenium
#=============================================================================
# shellcheck source=lib/common.sh
source "${SENTINELA_DIR}/lib/common.sh"
#=============================================================================
# User-Agents maliciosos
#=============================================================================
declare -A SCANNER_AGENTS
SCANNER_AGENTS=(
["sqlmap"]="sqlmap"
["nikto"]="nikto"
["nmap"]="nmap"
["masscan"]="masscan"
["zgrab"]="zgrab"
["zgrab2"]="zgrab2"
["acunetix"]="acunetix|Acunetix|AcuSensor|AcuMonitor"
["burpsuite"]="burpsuite|Burp Suite|BurpScanner|Burp-Proxy"
["netsparker"]="netsparker|Netsparker|NetSpark"
["appscan"]="AppScan|appscan"
["wpscan"]="wpscan|WPScan"
["joomscan"]="joomscan|JoomScan"
["droopescan"]="droopescan"
["gobuster"]="gobuster|GoBuster|Gobuster"
["dirbuster"]="dirbuster|DirBuster"
["dirsearch"]="dirsearch|Dirsearch"
["wfuzz"]="wfuzz|Wfuzz"
["ffuf"]="ffuf|Ffuf|Fuzz Faster"
["hydra"]="hydra|Hydra|THC-Hydra"
["medusa"]="medusa|Medusa"
["openvas"]="openvas|OpenVAS|Open Vas"
["nessus"]="nessus|Nessus|NessusSOAP"
["nexpose"]="nexpose|NeXpose|Nexpose"
["whatweb"]="whatweb|WhatWeb"
["nuclei"]="nuclei|Nuclei"
["httpx"]="httpx|Httpx|projectdiscovery"
["subfinder"]="subfinder"
["arachni"]="arachni|Arachni"
["w3af"]="w3af|w3af.org"
["skipfish"]="skipfish|SkipFish"
["gospider"]="gospider|GoSpider"
["hakrawler"]="hakrawler|Hakrawler"
["katana"]="katana|Katana"
["phantomjs"]="phantomjs|PhantomJS"
["selenium"]="selenium|Selenium"
["chrome_headless"]="HeadlessChrome|ChromeHeadless"
["python_requests"]="Python-requests|python-requests"
["python_urllib"]="Python-urllib|python-urllib"
["go_http"]="Go-http-client"
["java_client"]="Java/[0-9]|okhttp"
["ruby"]="ruby|Ruby"
["perl"]="libwww-perl|Perl"
["curl"]="^curl/"
["wget"]="^Wget/"
["api_client"]="axios|node-fetch|got/|superagent"
)
#=============================================================================
# Patrones de URI de escáneres
#=============================================================================
SCANNER_URI_PATTERNS=(
# Backup files
'\.bak$'
'\.old$'
'\.backup$'
'\.swp$'
'\.swo$'
'\.save$'
'\.orig$'
'\.dist$'
'\.tar\.gz$'
'\.zip$'
'\.tar$'
'\.tgz$'
'\.rar$'
'\.7z$'
# Config files
'/\.env'
'/\.env\.'
'/\.git/config'
'/\.svn/entries'
'/\.hg/store'
'/\.DS_Store'
'/Thumbs\.db'
'/crossdomain\.xml'
'/clientaccesspolicy\.xml'
# Admin panels
'/admin/'
'/administrator/'
'/manager/'
'/backend/'
'/cms/'
'/wp-admin/'
'/joomla/administrator'
'/drupal/admin'
'/magento/admin'
# PHPMyAdmin
'/phpmyadmin'
'/phpMyAdmin'
'/pma'
'/sqladmin'
'/mysqladmin'
'/phpmyadmin2'
# Common probes
'/server-status'
'/server-info'
'/phpinfo\.php'
'/info\.php'
'/test\.php'
'/debug\.php'
'/health'
'/health\.php'
'/status'
'/actuator'
'/actuator/health'
'/actuator/info'
'/actuator/env'
'/actuator/beans'
'/actuator/mappings'
# API probes
'/api/'
'/api/v1/'
'/api/v2/'
'/graphql'
'/swagger'
'/swagger-ui'
'/api-docs'
'/openapi'
'/docs/'
# Web shells
'/shell\.php'
'/cmd\.php'
'/exec\.php'
'/webshell\.php'
'/c99\.php'
'/r57\.php'
'/b374k\.php'
'/wso\.php'
'/alfanista\.php'
'/k1ll\.php'
'/symlink\.php'
# XML-RPC
'/xmlrpc\.php'
'/xmlrpc/'
'/XMLRPC'
# WordPress specific
'/wp-json/'
'/wp-content/'
'/wp-includes/'
'/wp-config\.php'
'/wp-config\.txt'
'/wp-config\.bak'
'/wp-config\.old'
'/wp-admin/admin-ajax\.php'
)
#=============================================================================
# Detectar escáner por User-Agent
#=============================================================================
scanner_check_agent() {
local ua="$1"
for tool in "${!SCANNER_AGENTS[@]}"; do
local pattern="${SCANNER_AGENTS[$tool]}"
if echo "${ua}" | grep -qiP "${pattern}"; then
echo "${tool}"
return 0
fi
done
return 1
}
#=============================================================================
# Detectar escáner por URI
#=============================================================================
scanner_check_uri() {
local uri="$1"
for pattern in "${SCANNER_URI_PATTERNS[@]}"; do
if echo "${uri}" | grep -qiP "${pattern}"; then
echo "${pattern}"
return 0
fi
done
return 1
}
#=============================================================================
# Detectar escáner por comportamiento (múltiples solicitudes rápidas)
#=============================================================================
scanner_check_behavior() {
local ip="$1"
local threshold="${2:-10}" # solicitudes en 1 minuto
local window="${3:-60}" # ventana en segundos
# Verificar si el IP está en el cache de comportamiento
local cache_file="${SENTINELA_DIR}/logs/.scanner_cache"
local now
now=$(date +%s)
# Crear cache si no existe
[[ -f "${cache_file}" ]] || touch "${cache_file}"
# Buscar IP en cache
local last_entry
last_entry=$(grep "^${ip} " "${cache_file}" 2>/dev/null || true)
if [[ -n "${last_entry}" ]]; then
local last_time count
last_time=$(echo "${last_entry}" | awk '{print $2}')
count=$(echo "${last_entry}" | awk '{print $3}')
if (( now - last_time < window )); then
count=$((count + 1))
if (( count >= threshold )); then
# Actualizar cache y retornar positivo
sed -i "s/^${ip} .*/${ip} ${now} ${count}/" "${cache_file}" 2>/dev/null || true
echo "behavior: ${count} requests in ${window}s"
return 0
fi
sed -i "s/^${ip} .*/${ip} ${now} ${count}/" "${cache_file}" 2>/dev/null || true
else
# Resetear contador (ventana expiró)
sed -i "s/^${ip} .*/${ip} ${now} 1/" "${cache_file}" 2>/dev/null || true
fi
else
# Agregar nuevo entry
echo "${ip} ${now} 1" >> "${cache_file}" 2>/dev/null || true
fi
return 1
}
#=============================================================================
# Limpiar cache de comportamiento
#=============================================================================
scanner_cleanup_cache() {
local cache_file="${SENTINELA_DIR}/logs/.scanner_cache"
local timeout="${1:-3600}" # 1 hora
if [[ -f "${cache_file}" ]]; then
local now
now=$(date +%s)
local cutoff=$((now - timeout))
# Remover entradas antiguas
awk -v cutoff="${cutoff}" '$2 >= cutoff' "${cache_file}" > "${cache_file}.tmp" 2>/dev/null || true
mv "${cache_file}.tmp" "${cache_file}" 2>/dev/null || true
fi
}
|