Script en Bash per a Ubuntu 24.04 (Noble) que fa un auditoria de vulnerabilitats i configuracions de seguretat sense canviar res del sistema. Detecta:
- Paquets amb actualitzacions de seguretat pendents
- CVE que afecten paquets instal·lats (via
debsecan, opcional) - Kernel i necessitat de reinici
- Estat de Livepatch/Ubuntu Pro
- Ports oberts i serveis escoltant
- Estat de firewall (UFW) i AppArmor
- Configuració SSH (riscos típics)
- Fitxers/directoris world-writable, binaris SUID/SGID
- Usuari(s) amb UID 0, sudoers sense contrasenya
- Actualitzacions automàtiques (unattended-upgrades)
- Paràmetres del sistema (core dumps, etc.)
🔒 Només detecció. No aplica canvis.
🧪 Opcionalment pot instal·lar eines lleugeres (debsecan,apparmor-utils,jq,mokutil, …) amb el flag--install-tools.
📝 Genera un informe Markdown + arxius adjunts amb detalls.
Ús ràpid
- Guarda el contingut a
ubuntu24_vuln_audit.sh - Dona execució i executa:
Shell
chmod +x ubuntu24_vuln_audit.sh
sudo ./ubuntu24_vuln_audit.sh --install-tools
Flags disponibles:
--install-tools→ instal·la eines recomanades per ampliar la detecció.--output <fitxer.md>→ especifica ruta de l’informe.--network-scan→ inclou escaneig local bàsic de ports (sense nmap).--quick→ omet comprovacions pesades (llistes llargues).--no-apt-update→ no faapt update(per entorns restringits).
Script: ubuntu24_vuln_audit.sh
#!/usr/bin/env bash
set -euo pipefail
# ------------------------------------------------------------
# Auditoria de vulnerabilitats i seguretat per a Ubuntu 24.04
# Ús:
# sudo ./ubuntu24_vuln_audit.sh [--install-tools] [--output informe.md] [--network-scan] [--quick] [--no-apt-update]
# ------------------------------------------------------------
# Colors
if command -v tput >/dev/null 2>&1; then
GREEN="$(tput setaf 2)"; YELLOW="$(tput setaf 3)"; RED="$(tput setaf 1)"; BLUE="$(tput setaf 4)"; BOLD="$(tput bold)"; RESET="$(tput sgr0)"
else
GREEN=""; YELLOW=""; RED=""; BLUE=""; BOLD=""; RESET=""
fi
# Flags i paràmetres
INSTALL_TOOLS=0
OUTPUT=""
NETWORK_SCAN=0
QUICK=0
APT_UPDATE=1
while [[ $# -gt 0 ]]; do
case "$1" in
--install-tools) INSTALL_TOOLS=1; shift ;;
--output) OUTPUT="$2"; shift 2 ;;
--network-scan) NETWORK_SCAN=1; shift ;;
--quick) QUICK=1; shift ;;
--no-apt-update) APT_UPDATE=0; shift ;;
*) echo "Paràmetre desconegut: $1"; exit 1 ;;
esac
done
# Requereix root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[ERROR]${RESET} Cal executar com a root (sudo)."
exit 1
fi
# Fitxers de sortida
HOST="$(hostname -s)"
STAMP="$(date +%F_%H%M%S)"
REPORT="${OUTPUT:-./audit_${HOST}_${STAMP}.md}"
OUTDIR="./audit_${HOST}_${STAMP}_files"
mkdir -p "$OUTDIR"
log() { echo -e "$1" | tee -a "$REPORT" >/dev/null; }
say() { echo -e "$1" | tee -a "$REPORT"; }
hr() { printf '\n---\n\n' | tee -a "$REPORT"; }
sec() { say "## $1"; }
sub() { say "### $1"; }
ok() { say "${GREEN}✔${RESET} $1"; }
warn() { say "${YELLOW}⚠${RESET} $1"; }
err() { say "${RED}✖${RESET} $1"; }
info() { say "${BLUE}ℹ${RESET} $1"; }
# Detectar OS
if [[ -r /etc/os-release ]]; then
. /etc/os-release
ID="${ID:-}"; VERSION_ID="${VERSION_ID:-}"
else
err "No es pot determinar el sistema. Falta /etc/os-release."
exit 1
fi
if [[ "$ID" != "ubuntu" ]]; then
warn "Sistema no Ubuntu (ID=$ID). L'script pot no ser exacte."
fi
if [[ "${VERSION_ID:-}" != "24.04" ]]; then
warn "Versió no 24.04 (VERSION_ID=${VERSION_ID:-desconeguda}). Continuo, però algunes comprovacions poden variar."
fi
# Capçalera informe
cat > "$REPORT" <<EOF
# Auditoria de Vulnerabilitats i Seguretat — Ubuntu $VERSION_ID ($NAME)
**Host:** $HOST
**Data:** $(date -Is)
**Usuari:** $(whoami)
> **Nota:** Auditoria només de lectura. No s'apliquen canvis. Algunes seccions guarden detalls a \`$OUTDIR\`.
EOF
hr
sec "1) Informació del sistema"
{
echo '```text'
echo "OS: $PRETTY_NAME"
command -v hostnamectl >/dev/null && hostnamectl
echo
echo "Kernel: $(uname -r) ($(uname -m))"
echo "Uptime: $(uptime -p 2>/dev/null || true)"
echo "CPU(s): $(nproc) | Memòria: $(free -h | awk '/Mem:/ {print $2\" total\"}')"
echo
echo "Particions (df -hT):"
df -hT
echo '```'
} | tee -a "$REPORT" >/dev/null
# Opcional: apt update per tenir informació d'actualitzacions
if [[ $APT_UPDATE -eq 1 ]]; then
sub "Actualitzant índex de paquets (apt update)"
if apt-get update -qq; then
ok "Índex de paquets actualitzat."
else
warn "No s'ha pogut actualitzar l'índex de paquets. Continuo amb dades locals."
fi
else
info "S'ha omès 'apt update' per indicació (--no-apt-update)."
fi
hr
sec "2) Actualitzacions i vulnerabilitats de paquets"
# Paquets actualitzables
sub "Paquets actualitzables"
apt list --upgradable 2>/dev/null | tee "$OUTDIR/upgradable.txt" >/dev/null || true
UPG_COUNT=$(grep -c 'upgradable from' "$OUTDIR/upgradable.txt" || true)
if [[ ${UPG_COUNT:-0} -gt 0 ]]; then
warn "$UPG_COUNT paquets amb actualitzacions disponibles."
say ""
say "<details><summary>Llista (clic per veure)</summary>"
say ""
say '```text'
sed -n '1,200p' "$OUTDIR/upgradable.txt" | sed 's/^/ /'
[[ $QUICK -eq 1 ]] && echo "... (mode ràpid: truncat)" || sed -n '201,9999p' "$OUTDIR/upgradable.txt" | sed 's/^/ /' || true
say '```'
say "</details>"
else
ok "No s'han trobat paquets actualitzables."
fi
# ubuntu-security-status (si disponible)
if command -v ubuntu-security-status >/dev/null 2>&1; then
sub "ubuntu-security-status"
ubuntu-security-status 2>&1 | tee "$OUTDIR/ubuntu-security-status.txt" >/dev/null || true
say ""
say "<details><summary>Sortida</summary>"
say ""
say '```text'
cat "$OUTDIR/ubuntu-security-status.txt"
say '```'
say "</details>"
else
info "`ubuntu-security-status` no està disponible (normalment a update-notifier-common)."
fi
# debsecan per CVEs (opcional)
if [[ $INSTALL_TOOLS -eq 1 ]]; then
sub "Instal·lant eines addicionals (debsecan, apparmor-utils, jq, mokutil)"
apt-get install -y -qq debsecan apparmor-utils jq mokutil >/dev/null || true
ok "Eines instal·lades (si no hi eren)."
fi
if command -v debsecan >/dev/null 2>&1; then
sub "CVE que afecten paquets instal·lats (debsecan)"
SUITE="${VERSION_CODENAME:-noble}"
# Llista de CVEs amb detall per a paquets instal·lats
debsecan --suite "$SUITE" --format detail --only-fixed 2>/dev/null | tee "$OUTDIR/debsecan_fixed.txt" >/dev/null || true
FIXED_COUNT=$(grep -c '^CVE-' "$OUTDIR/debsecan_fixed.txt" || true)
debsecan --suite "$SUITE" --format detail 2>/dev/null | tee "$OUTDIR/debsecan_all.txt" >/dev/null || true
ALL_COUNT=$(grep -c '^CVE-' "$OUTDIR/debsecan_all.txt" || true)
if [[ ${ALL_COUNT:-0} -gt 0 ]]; then
warn "CVE detectades: $ALL_COUNT total | amb correcció disponible: $FIXED_COUNT"
say ""
say "<details><summary>Resum CVE (debsecan)</summary>"
say ""
say '```text'
head -n $([[ $QUICK -eq 1 ]] && echo 100 || echo 500) "$OUTDIR/debsecan_all.txt"
[[ $QUICK -eq 1 ]] && echo "... (mode ràpid: truncat)"
say '```'
say "</details>"
else
ok "No s'han detectat CVE amb debsecan."
fi
else
info "Per llistat de CVE, instal·la debsecan (afegeix --install-tools)."
fi
hr
sec "3) Kernel i Livepatch"
# Kernel
CUR_KERNEL="$(uname -r)"
say "**Kernel actual:** \`$CUR_KERNEL\`"
if apt-cache policy linux-image-generic >/dev/null 2>&1; then
CANDIDATE="$(apt-cache policy linux-image-generic | awk '/Candidate:/ {print $2}')"
say "**Kernel meta-package candidate:** \`$CANDIDATE\`"
fi
if [[ -f /var/run/reboot-required ]]; then
warn "El sistema indica que cal reiniciar (fitxer /var/run/reboot-required present)."
else
ok "No hi ha senyal de reinici pendent."
fi
# Livepatch / Ubuntu Pro
if command -v pro >/dev/null 2>&1; then
sub "Ubuntu Pro / Livepatch (pro status)"
pro status || true | tee "$OUTDIR/pro_status.txt" >/dev/null
say ""
say "<details><summary>Sortida</summary>"
say ""
say '```text'
cat "$OUTDIR/pro_status.txt"
say '```'
say "</details>"
elif command -v canonical-livepatch >/dev/null 2>&1; then
sub "Canonical Livepatch"
canonical-livepatch status || true | tee "$OUTDIR/livepatch_status.txt" >/dev/null
say ""
say "<details><summary>Sortida</summary>"
say ""
say '```text'
cat "$OUTDIR/livepatch_status.txt"
say '```'
say "</details>"
else
info "Livepatch/Ubuntu Pro no detectat. (Opcional però recomanable per parches de kernel en calent)"
fi
hr
sec "4) Superfície d'atac (xarxa i serveis)"
sub "Serveis escoltant (ss -tulpen)"
ss -tulpen 2>/dev/null | tee "$OUTDIR/ss_listen.txt" >/dev/null || true
say ""
say "<details><summary>Sortida</summary>"
say ""
say '```text'
cat "$OUTDIR/ss_listen.txt"
say '```'
say "</details>"
if [[ $NETWORK_SCAN -eq 1 ]]; then
sub "Escaneig local bàsic de ports (127.0.0.1, sense nmap)"
# Prova ràpida per ports comuns
PORTS=(22 25 53 80 110 143 443 465 587 993 995 3306 5432 6379 8080 8443)
echo '```text' | tee -a "$REPORT"
for p in "${PORTS[@]}"; do
if timeout 0.5 bash -c "</dev/tcp/127.0.0.1/$p" 2>/dev/null; then
echo "Port $p obert" | tee -a "$REPORT"
fi
done
echo '```' | tee -a "$REPORT"
fi
hr
sec "5) Estat de Firewall i AppArmor"
# UFW
sub "UFW"
if command -v ufw >/dev/null 2>&1; then
ufw status verbose 2>&1 | tee "$OUTDIR/ufw_status.txt" >/dev/null || true
if grep -qE '^Status: active' "$OUTDIR/ufw_status.txt"; then
ok "UFW actiu."
else
warn "UFW no actiu."
fi
say ""
say "<details><summary>Regles UFW</summary>"
say ""
say '```text'
cat "$OUTDIR/ufw_status.txt"
say '```'
say "</details>"
else
info "UFW no instal·lat. Comprovo nftables (si disponible)."
if command -v nft >/dev/null 2>&1; then
nft list ruleset 2>/dev/null | tee "$OUTDIR/nft_ruleset.txt" >/dev/null || true
say "<details><summary>nftables ruleset</summary>"
say ""
say '```text'
cat "$OUTDIR/nft_ruleset.txt"
say '```'
say "</details>"
fi
fi
# AppArmor
sub "AppArmor"
if command -v apparmor_status >/dev/null 2>&1; then
apparmor_status 2>&1 | tee "$OUTDIR/apparmor_status.txt" >/dev/null || true
if grep -q "profiles are in enforce mode" "$OUTDIR/apparmor_status.txt"; then
ok "AppArmor amb perfils en mode enforce."
else
warn "AppArmor sense perfils en mode enforce o inactiu."
fi
say ""
say "<details><summary>Detall AppArmor</summary>"
say ""
say '```text'
cat "$OUTDIR/apparmor_status.txt"
say '```'
say "</details>"
else
info "Comanda apparmor_status no disponible (paquet apparmor-utils)."
fi
hr
sec "6) Configuració SSH i comptes"
# SSH efectiu (sshd -T)
sub "Configuració SSH"
SSH_WARN=0
if command -v sshd >/dev/null 2>&1; then
sshd -T 1>"$OUTDIR/sshd_effective.txt" 2>/dev/null || true
if [[ -s "$OUTDIR/sshd_effective.txt" ]]; then
say "<details><summary>Configuració efectiva (sshd -T)</summary>"
say ""
say '```text'
cat "$OUTDIR/sshd_effective.txt"
say '```'
say "</details>"
# Regles de risc comú
grep -qi '^permitrootlogin yes' "$OUTDIR/sshd_effective.txt" && { warn "SSH permet root login"; SSH_WARN=1; }
grep -qi '^passwordauthentication yes' "$OUTDIR/sshd_effective.txt" && { warn "SSH permet autenticació per contrasenya"; SSH_WARN=1; }
grep -qi '^challengeresponseauthentication yes' "$OUTDIR/sshd_effective.txt" && { warn "ChallengeResponseAuthentication habilitat"; SSH_WARN=1; }
grep -qi '^x11forwarding yes' "$OUTDIR/sshd_effective.txt" && { warn "X11Forwarding habilitat"; SSH_WARN=1; }
if [[ $SSH_WARN -eq 0 ]]; then ok "Configuració SSH sense riscos típics (root/password/X11)."; fi
else
info "No s'ha pogut obtenir la configuració efectiva amb sshd -T."
fi
else
info "sshd no disponible."
fi
# Comptes UID 0
sub "Comptes amb UID 0"
awk -F: '$3 == 0 {print $1}' /etc/passwd | tee "$OUTDIR/uid0.txt" >/dev/null
UID0_COUNT=$(wc -l < "$OUTDIR/uid0.txt")
if [[ $UID0_COUNT -gt 1 ]]; then
warn "Més d'un compte amb UID 0: $(tr '\n' ' ' < "$OUTDIR/uid0.txt")"
else
ok "Només root amb UID 0."
fi
# Sudoers NOPASSWD
sub "Regles sudo NOPASSWD"
( grep -R --line-number -E '^[^#].*NOPASSWD' /etc/sudoers /etc/sudoers.d 2>/dev/null || true ) | tee "$OUTDIR/sudo_nopasswd.txt" >/dev/null
if [[ -s "$OUTDIR/sudo_nopasswd.txt" ]]; then
warn "S'han trobat entrades NOPASSWD a sudoers (revisar fitxer adjunt)."
else
ok "No s'han trobat NOPASSWD a sudoers."
fi
hr
sec "7) Fitxers sensibles i permisos"
# /etc/passwd i /etc/shadow
sub "Permisos /etc/passwd i /etc/shadow"
PASSWD_PERM="$(stat -c '%a %U:%G' /etc/passwd)"
SHADOW_PERM="$(stat -c '%a %U:%G' /etc/shadow)"
say "- /etc/passwd: \`$PASSWD_PERM\` (esperat ~644 root:root)"
say "- /etc/shadow: \`$SHADOW_PERM\` (esperat ~640 root:shadow o 600 root:root)"
[[ "${PASSWD_PERM%% *}" != "644" ]] && warn "/etc/passwd amb permisos no estàndard."
[[ "${SHADOW_PERM%% *}" != "640" && "${SHADOW_PERM%% *}" != "600" ]] && warn "/etc/shadow amb permisos no estàndard."
# World-writable
sub "Directoris i fitxers world-writable (fora de /proc)"
find / -xdev -type d -perm -0002 -not -path "/proc/*" 2>/dev/null | tee "$OUTDIR/world_writable_dirs.txt" >/dev/null || true
find / -xdev -type f -perm -0002 -not -path "/proc/*" 2>/dev/null | tee "$OUTDIR/world_writable_files.txt" >/dev/null || true
WWD_COUNT=$(wc -l < "$OUTDIR/world_writable_dirs.txt" || echo 0)
WWF_COUNT=$(wc -l < "$OUTDIR/world_writable_files.txt" || echo 0)
if [[ $WWD_COUNT -gt 0 || $WWF_COUNT -gt 0 ]]; then
warn "World-writable: $WWD_COUNT directoris | $WWF_COUNT fitxers (veure adjunts)."
else
ok "No s'han detectat world-writable fora de /proc."
fi
# SUID/SGID
sub "Binaris SUID/SGID"
find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null | tee "$OUTDIR/suid_sgid.txt" >/dev/null || true
SUID_COUNT=$(wc -l < "$OUTDIR/suid_sgid.txt" || echo 0)
if [[ $SUID_COUNT -gt 0 ]]; then
warn "$SUID_COUNT binaris SUID/SGID (revisar; alguns són normals)."
if [[ $QUICK -eq 1 ]]; then
say ""
say "<details><summary>Mostra (primeres 50 línies)</summary>"
say ""
say '```text'
head -n 50 "$OUTDIR/suid_sgid.txt"
say '```'
say "</details>"
else
say ""
say "<details><summary>Llista completa</summary>"
say ""
say '```text'
cat "$OUTDIR/suid_sgid.txt"
say '```'
say "</details>"
fi
else
ok "No s'han trobat SUID/SGID (poc habitual)."
fi
hr
sec "8) Paràmetres del sistema"
sub "Core dumps i suid_dumpable"
SYS_CORE="$(sysctl -n kernel.core_pattern 2>/dev/null || echo '?')"
SYS_SUID="$(sysctl -n fs.suid_dumpable 2>/dev/null || echo '?')"
say "- kernel.core_pattern: \`$SYS_CORE\`"
say "- fs.suid_dumpable: \`$SYS_SUID\` (0 recomanat)"
[[ "$SYS_SUID" != "0" ]] && warn "fs.suid_dumpable no és 0."
# Secure Boot
sub "Secure Boot"
if command -v mokutil >/dev/null 2>&1; then
SB_STATE="$(mokutil --sb-state 2>/dev/null || true)"
say '```text'
echo "$SB_STATE"
say '```'
echo "$SB_STATE" | grep -qi 'enabled' && ok "Secure Boot habilitat." || warn "Secure Boot deshabilitat."
else
info "mokutil no disponible (afegeix --install-tools per comprovar Secure Boot)."
fi
hr
sec "9) Resum i recomanacions"
say "- **Actualitzables:** $UPG_COUNT paquets."
if [[ -n "${ALL_COUNT:-}" ]]; then
say "- **CVE totals (debsecan):** ${ALL_COUNT:-0} | **Amb fix disponible:** ${FIXED_COUNT:-0}"
fi
[[ -f /var/run/reboot-required ]] && say "- **Reinici pendent:** Sí" || say "- **Reinici pendent:** No"
say ""
say "### Recomanacions generals"
say "1. Aplicar **actualitzacions de seguretat** immediates: \`sudo apt-get upgrade\` (o \`unattended-upgrades\`)."
say "2. Si exposeu serveis, habilitar i configurar **UFW** i assegurar **AppArmor**."
say "3. **SSH**: deshabilitar root login i autenticació per contrasenya si és possible (usar claus)."
say "4. Revisar **SUID/SGID** i **world-writable** innecessaris."
say "5. Considerar **Ubuntu Pro/Livepatch** per a parches de kernel en calent."
say "6. Revisar **NOPASSWD** a sudoers i perfils d'usuari amb privilegis."
hr
ok "Informe generat: $REPORT"
info "Detalls i adjunts a: $OUTDIR"
``
Què obtindràs
- Un informe Markdown amb seccions clares per a decisió ràpida.
- Arxius adjunts amb detall complet (llistes de ports, CVE, SUID/SGID, etc.).
- Cap canvi al sistema (excepte si passes
--install-tools, que només instal·la utilitats de lectura).