Téléverser les fichiers vers "/"
This commit is contained in:
102
README.md
Normal file
102
README.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# Audit SUID GUID Linux
|
||||||
|
|
||||||
|
## Shell
|
||||||
|
### Installation
|
||||||
|
Copier audit-suid.sh
|
||||||
|
Exemple
|
||||||
|
```
|
||||||
|
cp audit-suid.sh /usr/local/bin/audit-suid.sh
|
||||||
|
chmod +x /usr/local/bin/audit-suid.sh
|
||||||
|
```
|
||||||
|
### Exécution
|
||||||
|
```
|
||||||
|
# Exécuter avec les droits root
|
||||||
|
sudo /usr/local/bin/audit-suid.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Planification avec cron
|
||||||
|
```
|
||||||
|
# Audit quotidien à 2h du matin
|
||||||
|
sudo crontab -e
|
||||||
|
# Ajouter la ligne :
|
||||||
|
0 2 * * * /usr/local/bin/audit-suid.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Personalisation
|
||||||
|
|
||||||
|
Modifiez le fichier /etc/audit/suid_whitelist.conf pour ajouter vos propres binaires légitimes.
|
||||||
|
|
||||||
|
Ce script vous aidera à maintenir la sécurité de vos serveurs Linux en identifiant rapidement les risques potentiels liés aux permissions SUID/SGID.
|
||||||
|
|
||||||
|
## Pyhton
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Copier audit-suid.py
|
||||||
|
Exemple
|
||||||
|
|
||||||
|
```
|
||||||
|
# Sauvegarde du script
|
||||||
|
sudo mkdir -p /usr/local/bin/
|
||||||
|
sudo cp suid_audit.py /usr/local/bin/audit-suid.py
|
||||||
|
sudo chmod +x /usr/local/bin/audit-suid.py
|
||||||
|
# Création des répertoires
|
||||||
|
sudo mkdir -p /etc/audit /var/log/audit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Utilisation
|
||||||
|
```
|
||||||
|
# Exécution simple
|
||||||
|
sudo python3 audit-suid.py
|
||||||
|
|
||||||
|
# Avec options
|
||||||
|
sudo python3 audit-suid.py --max-depth 15 --no-temp
|
||||||
|
|
||||||
|
# Aide
|
||||||
|
python3 audit-suid.py --help
|
||||||
|
|
||||||
|
# Exécution avec droits root (obligatoire)
|
||||||
|
sudo /usr/local/bin/audit-suid.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options disponibles
|
||||||
|
Option Description
|
||||||
|
|
||||||
|
--no-temp Ne pas vérifier /tmp, /var/tmp, /dev/shm
|
||||||
|
|
||||||
|
--no-orphan Ne pas vérifier les fichiers orphelins
|
||||||
|
|
||||||
|
--no-hash Ne pas calculer les hashs MD5 (plus rapide)
|
||||||
|
|
||||||
|
--max-depth N Profondeur maximale de recherche (défaut: 20)
|
||||||
|
|
||||||
|
--log-dir PATH Répertoire des logs (défaut: /var/log/audit)
|
||||||
|
|
||||||
|
--whitelist PATH Fichier JSON de whitelist
|
||||||
|
|
||||||
|
### Cron
|
||||||
|
```
|
||||||
|
# Éditer crontab
|
||||||
|
sudo crontab -e
|
||||||
|
|
||||||
|
# Ajouter pour une exécution quotidienne à 2h
|
||||||
|
0 2 * * * /usr/local/bin/audit-suid.py --no-hash
|
||||||
|
|
||||||
|
# Exécution hebdomadaire avec tous les contrôles
|
||||||
|
0 3 * * 0 /usr/local/bin/audit-suid.py
|
||||||
|
```
|
||||||
|
### Format du fichier de whitelist (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"SUID": [
|
||||||
|
"/bin/su",
|
||||||
|
"/usr/bin/sudo",
|
||||||
|
"/usr/bin/passwd"
|
||||||
|
],
|
||||||
|
"SGID": [
|
||||||
|
"/usr/bin/wall",
|
||||||
|
"/usr/bin/write"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
582
audit-suid.py
Normal file
582
audit-suid.py
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Script d'audit des fichiers SUID et GUID pour Linux
|
||||||
|
Sans dépendances externes
|
||||||
|
Auteur: Zen6
|
||||||
|
Version: 1.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import stat
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Set, Tuple
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
@dataclass
|
||||||
|
class AuditConfig:
|
||||||
|
log_dir: str = "/var/log/audit"
|
||||||
|
whitelist_file: str = "/etc/audit/suid_whitelist.json"
|
||||||
|
excluded_dirs: List[str] = None
|
||||||
|
max_depth: int = 20
|
||||||
|
check_temp_dirs: bool = True
|
||||||
|
check_orphans: bool = True
|
||||||
|
compute_hashes: bool = True
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.excluded_dirs is None:
|
||||||
|
self.excluded_dirs = [
|
||||||
|
"/proc", "/sys", "/dev", "/run", "/snap",
|
||||||
|
"/var/lib/docker", "/var/lib/lxcfs", "/proc/*",
|
||||||
|
"/sys/*", "/dev/*"
|
||||||
|
]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FileAudit:
|
||||||
|
path: str
|
||||||
|
type: str
|
||||||
|
permissions: str
|
||||||
|
owner: str
|
||||||
|
group: str
|
||||||
|
size: int
|
||||||
|
size_human: str
|
||||||
|
mtime: str
|
||||||
|
hash_md5: str = ""
|
||||||
|
is_whitelisted: bool = False
|
||||||
|
alert_level: str = "INFO"
|
||||||
|
|
||||||
|
class SUIDGUIDAuditor:
|
||||||
|
def __init__(self, config: AuditConfig = None):
|
||||||
|
self.config = config or AuditConfig()
|
||||||
|
self.whitelist = {"SUID": set(), "SGID": set()}
|
||||||
|
self.alerts = []
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
# Configuration du logging
|
||||||
|
self.setup_logging()
|
||||||
|
|
||||||
|
def setup_logging(self):
|
||||||
|
"""Configure le système de logging"""
|
||||||
|
# Création du répertoire de logs
|
||||||
|
Path(self.config.log_dir).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Nom des fichiers de log
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
self.log_file = Path(self.config.log_dir) / f"audit_{timestamp}.log"
|
||||||
|
self.report_file = Path(self.config.log_dir) / f"report_{timestamp}.txt"
|
||||||
|
self.alert_file = Path(self.config.log_dir) / f"alerts_{timestamp}.txt"
|
||||||
|
self.json_file = Path(self.config.log_dir) / f"audit_{timestamp}.json"
|
||||||
|
self.csv_file = Path(self.config.log_dir) / f"audit_{timestamp}.csv"
|
||||||
|
|
||||||
|
# Configuration du logging (sans couleurs)
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(self.log_file),
|
||||||
|
logging.StreamHandler()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def load_whitelist(self):
|
||||||
|
"""Charge la liste blanche depuis un fichier JSON"""
|
||||||
|
try:
|
||||||
|
if Path(self.config.whitelist_file).exists():
|
||||||
|
with open(self.config.whitelist_file, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
self.whitelist["SUID"] = set(data.get("SUID", []))
|
||||||
|
self.whitelist["SGID"] = set(data.get("SGID", []))
|
||||||
|
self.logger.info(f"Liste blanche chargée: {len(self.whitelist['SUID'])} SUID, {len(self.whitelist['SGID'])} SGID")
|
||||||
|
else:
|
||||||
|
self.create_default_whitelist()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Erreur lors du chargement de la whitelist: {e}")
|
||||||
|
|
||||||
|
def create_default_whitelist(self):
|
||||||
|
"""Crée une liste blanche par défaut"""
|
||||||
|
default_whitelist = {
|
||||||
|
"SUID": [
|
||||||
|
"/bin/su", "/bin/ping", "/bin/mount", "/bin/umount",
|
||||||
|
"/usr/bin/passwd", "/usr/bin/sudo", "/usr/bin/chsh",
|
||||||
|
"/usr/bin/chfn", "/usr/bin/gpasswd", "/usr/bin/crontab",
|
||||||
|
"/usr/bin/at", "/usr/bin/newgrp", "/usr/sbin/unix_chkpwd",
|
||||||
|
"/usr/lib/openssh/ssh-keysign", "/usr/lib/dbus-1.0/dbus-daemon-launch-helper",
|
||||||
|
"/usr/bin/sg", "/usr/bin/pkexec", "/usr/bin/ksu"
|
||||||
|
],
|
||||||
|
"SGID": [
|
||||||
|
"/usr/bin/wall", "/usr/bin/write", "/usr/bin/ssh-agent",
|
||||||
|
"/usr/bin/locate", "/usr/bin/mlocate", "/usr/bin/bsd-write",
|
||||||
|
"/usr/bin/chage", "/usr/bin/expiry"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
Path(self.config.whitelist_file).parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(self.config.whitelist_file, 'w') as f:
|
||||||
|
json.dump(default_whitelist, f, indent=4)
|
||||||
|
self.whitelist["SUID"] = set(default_whitelist["SUID"])
|
||||||
|
self.whitelist["SGID"] = set(default_whitelist["SGID"])
|
||||||
|
self.logger.info(f"Liste blanche par défaut créée: {self.config.whitelist_file}")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Erreur lors de la création de la whitelist: {e}")
|
||||||
|
|
||||||
|
def get_file_info(self, filepath: str) -> Tuple[str, str, str, int, str]:
|
||||||
|
"""Récupère les informations d'un fichier"""
|
||||||
|
try:
|
||||||
|
stat_info = os.stat(filepath, follow_symlinks=False)
|
||||||
|
permissions = stat.filemode(stat_info.st_mode)
|
||||||
|
owner = str(stat_info.st_uid)
|
||||||
|
group = str(stat_info.st_gid)
|
||||||
|
size = stat_info.st_size
|
||||||
|
mtime = datetime.fromtimestamp(stat_info.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# Récupérer les noms d'utilisateur/groupe si possible
|
||||||
|
try:
|
||||||
|
import pwd
|
||||||
|
owner = pwd.getpwuid(stat_info.st_uid).pw_name
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import grp
|
||||||
|
group = grp.getgrgid(stat_info.st_gid).gr_name
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return permissions, owner, group, size, mtime
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug(f"Erreur sur {filepath}: {e}")
|
||||||
|
return "unknown", "unknown", "unknown", 0, "unknown"
|
||||||
|
|
||||||
|
def compute_md5(self, filepath: str) -> str:
|
||||||
|
"""Calcule le hash MD5 d'un fichier"""
|
||||||
|
if not self.config.compute_hashes:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
hash_md5 = hashlib.md5()
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
for chunk in iter(lambda: f.read(8192), b""):
|
||||||
|
hash_md5.update(chunk)
|
||||||
|
return hash_md5.hexdigest()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug(f"Erreur MD5 sur {filepath}: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def check_suid_sgid(self, filepath: str) -> List[str]:
|
||||||
|
"""Vérifie les bits SUID et SGID"""
|
||||||
|
try:
|
||||||
|
st = os.stat(filepath, follow_symlinks=False)
|
||||||
|
mode = st.st_mode
|
||||||
|
types = []
|
||||||
|
if mode & stat.S_ISUID:
|
||||||
|
types.append("SUID")
|
||||||
|
if mode & stat.S_ISGID:
|
||||||
|
types.append("SGID")
|
||||||
|
return types
|
||||||
|
except:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def should_exclude(self, filepath: str) -> bool:
|
||||||
|
"""Vérifie si le chemin doit être exclu"""
|
||||||
|
for excluded in self.config.excluded_dirs:
|
||||||
|
if filepath.startswith(excluded):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def audit_directory(self, start_path: str = "/"):
|
||||||
|
"""Audite récursivement un répertoire"""
|
||||||
|
self.logger.info(f"Début de l'audit depuis {start_path}")
|
||||||
|
total_files = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
for root, dirs, files in os.walk(start_path, followlinks=False):
|
||||||
|
# Filtrer les répertoires exclus
|
||||||
|
dirs[:] = [d for d in dirs if not self.should_exclude(os.path.join(root, d))]
|
||||||
|
|
||||||
|
# Vérifier la profondeur maximale
|
||||||
|
depth = root.count(os.sep)
|
||||||
|
if depth > self.config.max_depth:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
total_files += 1
|
||||||
|
|
||||||
|
if total_files % 10000 == 0:
|
||||||
|
self.logger.info(f"Progression: {total_files} fichiers parcourus...")
|
||||||
|
|
||||||
|
suid_sgid_types = self.check_suid_sgid(filepath)
|
||||||
|
if suid_sgid_types:
|
||||||
|
self.process_file(filepath, suid_sgid_types)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
self.logger.warning("Audit interrompu par l'utilisateur")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Erreur lors de l'audit: {e}")
|
||||||
|
|
||||||
|
self.logger.info(f"Audit terminé. {total_files} fichiers parcourus.")
|
||||||
|
|
||||||
|
def process_file(self, filepath: str, types: List[str]):
|
||||||
|
"""Traite un fichier avec bits SUID/SGID"""
|
||||||
|
permissions, owner, group, size, mtime = self.get_file_info(filepath)
|
||||||
|
|
||||||
|
for file_type in types:
|
||||||
|
is_whitelisted = filepath in self.whitelist.get(file_type, set())
|
||||||
|
alert_level = "INFO" if is_whitelisted else "WARNING"
|
||||||
|
|
||||||
|
hash_md5 = ""
|
||||||
|
if not is_whitelisted and self.config.compute_hashes:
|
||||||
|
hash_md5 = self.compute_md5(filepath)
|
||||||
|
|
||||||
|
audit = FileAudit(
|
||||||
|
path=filepath,
|
||||||
|
type=file_type,
|
||||||
|
permissions=permissions,
|
||||||
|
owner=owner,
|
||||||
|
group=group,
|
||||||
|
size=size,
|
||||||
|
size_human=self.human_readable_size(size),
|
||||||
|
mtime=mtime,
|
||||||
|
hash_md5=hash_md5,
|
||||||
|
is_whitelisted=is_whitelisted,
|
||||||
|
alert_level=alert_level
|
||||||
|
)
|
||||||
|
|
||||||
|
self.results.append(audit)
|
||||||
|
|
||||||
|
if is_whitelisted:
|
||||||
|
self.logger.info(f"[OK {file_type}] {filepath}")
|
||||||
|
else:
|
||||||
|
self.logger.warning(f"[ALERTE {file_type}] {filepath} (propriétaire: {owner}, groupe: {group})")
|
||||||
|
self.alerts.append(audit)
|
||||||
|
|
||||||
|
def check_temp_directories(self):
|
||||||
|
"""Vérifie les répertoires temporaires"""
|
||||||
|
if not self.config.check_temp_dirs:
|
||||||
|
return
|
||||||
|
|
||||||
|
temp_dirs = ["/tmp", "/var/tmp", "/dev/shm"]
|
||||||
|
|
||||||
|
self.logger.info("Vérification des répertoires temporaires...")
|
||||||
|
|
||||||
|
for temp_dir in temp_dirs:
|
||||||
|
if os.path.exists(temp_dir):
|
||||||
|
try:
|
||||||
|
for root, dirs, files in os.walk(temp_dir):
|
||||||
|
for file in files:
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
types = self.check_suid_sgid(filepath)
|
||||||
|
if types:
|
||||||
|
self.logger.error(f"[URGENT] Fichier SUID/SGID dans {temp_dir}: {filepath}")
|
||||||
|
alert = FileAudit(
|
||||||
|
path=filepath,
|
||||||
|
type=",".join(types),
|
||||||
|
permissions="unknown",
|
||||||
|
owner="unknown",
|
||||||
|
group="unknown",
|
||||||
|
size=0,
|
||||||
|
size_human="0B",
|
||||||
|
mtime="unknown",
|
||||||
|
alert_level="CRITICAL"
|
||||||
|
)
|
||||||
|
self.alerts.append(alert)
|
||||||
|
self.results.append(alert)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug(f"Erreur lors de la vérification de {temp_dir}: {e}")
|
||||||
|
|
||||||
|
def check_orphan_files(self):
|
||||||
|
"""Vérifie les fichiers sans propriétaire valide"""
|
||||||
|
if not self.config.check_orphans:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.logger.info("Recherche des fichiers orphelins...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = "find / -type f \\( -perm -4000 -o -perm -2000 \\) \\( -nouser -o -nogroup \\) 2>/dev/null"
|
||||||
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=300)
|
||||||
|
|
||||||
|
for line in result.stdout.splitlines():
|
||||||
|
if line.strip():
|
||||||
|
self.logger.error(f"[ORPHELIN] {line}")
|
||||||
|
alert = FileAudit(
|
||||||
|
path=line.strip(),
|
||||||
|
type="ORPHAN",
|
||||||
|
permissions="unknown",
|
||||||
|
owner="unknown",
|
||||||
|
group="unknown",
|
||||||
|
size=0,
|
||||||
|
size_human="0B",
|
||||||
|
mtime="unknown",
|
||||||
|
alert_level="CRITICAL"
|
||||||
|
)
|
||||||
|
self.alerts.append(alert)
|
||||||
|
self.results.append(alert)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
self.logger.error("Timeout lors de la recherche des fichiers orphelins")
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Erreur lors de la vérification des orphelins: {e}")
|
||||||
|
|
||||||
|
def human_readable_size(self, size: int) -> str:
|
||||||
|
"""Convertit la taille en format lisible"""
|
||||||
|
if size == 0:
|
||||||
|
return "0 B"
|
||||||
|
|
||||||
|
units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||||
|
i = 0
|
||||||
|
while size >= 1024 and i < len(units) - 1:
|
||||||
|
size /= 1024.0
|
||||||
|
i += 1
|
||||||
|
return f"{size:.2f} {units[i]}"
|
||||||
|
|
||||||
|
def generate_reports(self):
|
||||||
|
"""Génère tous les rapports (texte, JSON, CSV)"""
|
||||||
|
# Rapport texte détaillé
|
||||||
|
self.generate_text_report()
|
||||||
|
|
||||||
|
# Rapport JSON
|
||||||
|
self.generate_json_report()
|
||||||
|
|
||||||
|
# Rapport CSV
|
||||||
|
self.generate_csv_report()
|
||||||
|
|
||||||
|
self.logger.info(f"Rapport texte généré: {self.report_file}")
|
||||||
|
self.logger.info(f"Rapport JSON généré: {self.json_file}")
|
||||||
|
self.logger.info(f"Rapport CSV généré: {self.csv_file}")
|
||||||
|
self.logger.info(f"Alertes sauvegardées: {self.alert_file}")
|
||||||
|
|
||||||
|
def generate_text_report(self):
|
||||||
|
"""Génère un rapport texte détaillé"""
|
||||||
|
with open(self.report_file, 'w') as f:
|
||||||
|
f.write("=" * 80 + "\n")
|
||||||
|
f.write("RAPPORT D'AUDIT SUID/SGID\n")
|
||||||
|
f.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||||
|
f.write(f"Serveur: {os.uname().nodename}\n")
|
||||||
|
f.write(f"Système: {os.uname().sysname} {os.uname().release}\n")
|
||||||
|
f.write("=" * 80 + "\n\n")
|
||||||
|
|
||||||
|
# Résumé
|
||||||
|
f.write("RÉSUMÉ\n")
|
||||||
|
f.write("-" * 40 + "\n")
|
||||||
|
f.write(f"Total fichiers audités: {len(self.results)}\n")
|
||||||
|
f.write(f"Anomalies SUID: {len([r for r in self.alerts if r.type == 'SUID'])}\n")
|
||||||
|
f.write(f"Anomalies SGID: {len([r for r in self.alerts if r.type == 'SGID'])}\n")
|
||||||
|
f.write(f"Fichiers orphelins: {len([r for r in self.alerts if r.type == 'ORPHAN'])}\n")
|
||||||
|
f.write(f"Fichiers whitelistés: {len([r for r in self.results if r.is_whitelisted])}\n\n")
|
||||||
|
|
||||||
|
# Anomalies détaillées
|
||||||
|
if self.alerts:
|
||||||
|
f.write("ANOMALIES DÉTECTÉES\n")
|
||||||
|
f.write("-" * 40 + "\n")
|
||||||
|
|
||||||
|
# Séparer par type
|
||||||
|
suid_alerts = [a for a in self.alerts if a.type == 'SUID']
|
||||||
|
sgid_alerts = [a for a in self.alerts if a.type == 'SGID']
|
||||||
|
orphan_alerts = [a for a in self.alerts if a.type == 'ORPHAN']
|
||||||
|
|
||||||
|
if suid_alerts:
|
||||||
|
f.write("\n[SUID SUSPECTS]\n")
|
||||||
|
for i, alert in enumerate(suid_alerts, 1):
|
||||||
|
f.write(f"\n{i}. {alert.path}\n")
|
||||||
|
f.write(f" Type: {alert.type}\n")
|
||||||
|
f.write(f" Propriétaire: {alert.owner}\n")
|
||||||
|
f.write(f" Groupe: {alert.group}\n")
|
||||||
|
f.write(f" Permissions: {alert.permissions}\n")
|
||||||
|
f.write(f" Taille: {alert.size_human}\n")
|
||||||
|
f.write(f" Dernière modification: {alert.mtime}\n")
|
||||||
|
if alert.hash_md5:
|
||||||
|
f.write(f" MD5: {alert.hash_md5}\n")
|
||||||
|
|
||||||
|
if sgid_alerts:
|
||||||
|
f.write("\n[SGID SUSPECTS]\n")
|
||||||
|
for i, alert in enumerate(sgid_alerts, 1):
|
||||||
|
f.write(f"\n{i}. {alert.path}\n")
|
||||||
|
f.write(f" Type: {alert.type}\n")
|
||||||
|
f.write(f" Propriétaire: {alert.owner}\n")
|
||||||
|
f.write(f" Groupe: {alert.group}\n")
|
||||||
|
f.write(f" Permissions: {alert.permissions}\n")
|
||||||
|
f.write(f" Taille: {alert.size_human}\n")
|
||||||
|
f.write(f" Dernière modification: {alert.mtime}\n")
|
||||||
|
if alert.hash_md5:
|
||||||
|
f.write(f" MD5: {alert.hash_md5}\n")
|
||||||
|
|
||||||
|
if orphan_alerts:
|
||||||
|
f.write("\n[FICHIERS ORPHELINS]\n")
|
||||||
|
for i, alert in enumerate(orphan_alerts, 1):
|
||||||
|
f.write(f"\n{i}. {alert.path}\n")
|
||||||
|
f.write(f" Type: {alert.type}\n")
|
||||||
|
else:
|
||||||
|
f.write("✓ AUCUNE ANOMALIE DÉTECTÉE\n\n")
|
||||||
|
|
||||||
|
# Tous les fichiers SUID/SGID (y compris whitelist)
|
||||||
|
f.write("\n\nLISTE COMPLÈTE DES FICHIERS SUID/SGID\n")
|
||||||
|
f.write("-" * 40 + "\n")
|
||||||
|
|
||||||
|
for result in sorted(self.results, key=lambda x: x.path):
|
||||||
|
status = "[WHITELIST]" if result.is_whitelisted else "[ALERTE]"
|
||||||
|
f.write(f"{status} {result.type}: {result.path}\n")
|
||||||
|
f.write(f" Permissions: {result.permissions}, Owner: {result.owner}, Group: {result.group}\n")
|
||||||
|
|
||||||
|
# Recommandations
|
||||||
|
f.write("\n\nRECOMMANDATIONS\n")
|
||||||
|
f.write("-" * 40 + "\n")
|
||||||
|
f.write("1. Examinez chaque binaire suspect manuellement\n")
|
||||||
|
f.write("2. Supprimez les bits SUID/SGID si non nécessaires:\n")
|
||||||
|
f.write(" sudo chmod u-s <fichier> # Pour SUID\n")
|
||||||
|
f.write(" sudo chmod g-s <fichier> # Pour SGID\n")
|
||||||
|
f.write(f"3. Ajoutez les binaires légitimes à la whitelist:\n")
|
||||||
|
f.write(f" {self.config.whitelist_file}\n")
|
||||||
|
f.write("4. Vérifiez l'intégrité des binaires système\n")
|
||||||
|
f.write("5. Mettez en place une surveillance régulière\n")
|
||||||
|
|
||||||
|
def generate_json_report(self):
|
||||||
|
"""Génère un rapport JSON"""
|
||||||
|
report_data = {
|
||||||
|
"metadata": {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"hostname": os.uname().nodename,
|
||||||
|
"system": f"{os.uname().sysname} {os.uname().release}",
|
||||||
|
"total_files_audited": len(self.results),
|
||||||
|
"total_alerts": len(self.alerts)
|
||||||
|
},
|
||||||
|
"alerts": [asdict(alert) for alert in self.alerts],
|
||||||
|
"all_files": [asdict(result) for result in self.results]
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(self.json_file, 'w') as f:
|
||||||
|
json.dump(report_data, f, indent=2)
|
||||||
|
|
||||||
|
def generate_csv_report(self):
|
||||||
|
"""Génère un rapport CSV pour analyse avec tableur"""
|
||||||
|
import csv
|
||||||
|
|
||||||
|
with open(self.csv_file, 'w', newline='') as f:
|
||||||
|
if self.results:
|
||||||
|
fieldnames = ['path', 'type', 'permissions', 'owner', 'group',
|
||||||
|
'size', 'size_human', 'mtime', 'hash_md5',
|
||||||
|
'is_whitelisted', 'alert_level']
|
||||||
|
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
for result in self.results:
|
||||||
|
writer.writerow(asdict(result))
|
||||||
|
|
||||||
|
def save_alerts_file(self):
|
||||||
|
"""Sauvegarde les alertes dans un fichier simple"""
|
||||||
|
with open(self.alert_file, 'w') as f:
|
||||||
|
f.write("# Fichier d'alertes SUID/SGID\n")
|
||||||
|
f.write(f"# Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||||
|
f.write("# Format: TYPE|PATH|OWNER|GROUP|LEVEL\n\n")
|
||||||
|
|
||||||
|
for alert in self.alerts:
|
||||||
|
f.write(f"{alert.type}|{alert.path}|{alert.owner}|{alert.group}|{alert.alert_level}\n")
|
||||||
|
|
||||||
|
def print_summary(self) -> int:
|
||||||
|
"""Affiche un résumé dans la console"""
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("RÉSUMÉ DE L'AUDIT")
|
||||||
|
print("=" * 80)
|
||||||
|
print(f"Log complet: {self.log_file}")
|
||||||
|
print(f"Rapport détaillé: {self.report_file}")
|
||||||
|
print(f"Rapport JSON: {self.json_file}")
|
||||||
|
print(f"Rapport CSV: {self.csv_file}")
|
||||||
|
print(f"Alertes: {self.alert_file}")
|
||||||
|
print(f"Whitelist: {self.config.whitelist_file}")
|
||||||
|
|
||||||
|
if self.alerts:
|
||||||
|
print(f"\n⚠️ ATTENTION: {len(self.alerts)} anomalie(s) détectée(s) !")
|
||||||
|
print(f" - SUID suspects: {len([a for a in self.alerts if a.type == 'SUID'])}")
|
||||||
|
print(f" - SGID suspects: {len([a for a in self.alerts if a.type == 'SGID'])}")
|
||||||
|
print(f" - Orphelins: {len([a for a in self.alerts if a.type == 'ORPHAN'])}")
|
||||||
|
print("Consultez les rapports pour plus de détails")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
print("\n✓ AUCUNE ANOMALIE DÉTECTÉE")
|
||||||
|
print("Tous les fichiers SUID/SGID sont dans la whitelist")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def run(self) -> int:
|
||||||
|
"""Exécute l'audit complet"""
|
||||||
|
print("=" * 80)
|
||||||
|
print("SUID/SGID Security Audit Tool - Linux Privilege Scanner")
|
||||||
|
print(f"Début de l'audit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# Vérification des droits root
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
self.logger.error("Ce script doit être exécuté en tant que root")
|
||||||
|
print("ERREUR: Ce script doit être exécuté en tant que root")
|
||||||
|
print(f"Utilisez: sudo python3 {sys.argv[0]}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Chargement de la configuration
|
||||||
|
self.load_whitelist()
|
||||||
|
|
||||||
|
# Exécution des audits
|
||||||
|
self.audit_directory()
|
||||||
|
self.check_temp_directories()
|
||||||
|
self.check_orphan_files()
|
||||||
|
|
||||||
|
# Génération des rapports
|
||||||
|
self.generate_reports()
|
||||||
|
self.save_alerts_file()
|
||||||
|
|
||||||
|
# Affichage du résumé
|
||||||
|
return self.print_summary()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Fonction principale avec parsing des arguments"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Audit des fichiers SUID et GUID sur Linux",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Exemples:
|
||||||
|
sudo python3 audit-suid.py # Audit complet
|
||||||
|
sudo python3 audit-suid.py --no-temp # Sans vérification /tmp
|
||||||
|
sudo python3 audit-suid.py --max-depth 10 # Limite à 10 niveaux
|
||||||
|
sudo python3 audit-suid.py --no-hash # Sans calcul MD5 (plus rapide)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
parser.add_argument('--no-temp', action='store_true',
|
||||||
|
help="Ne pas vérifier les répertoires temporaires")
|
||||||
|
parser.add_argument('--no-orphan', action='store_true',
|
||||||
|
help="Ne pas vérifier les fichiers orphelins")
|
||||||
|
parser.add_argument('--no-hash', action='store_true',
|
||||||
|
help="Ne pas calculer les hashs MD5 (plus rapide)")
|
||||||
|
parser.add_argument('--max-depth', type=int, default=20,
|
||||||
|
help="Profondeur maximale de recherche (défaut: 20)")
|
||||||
|
parser.add_argument('--log-dir', type=str, default="/var/log/audit",
|
||||||
|
help="Répertoire des logs (défaut: /var/log/audit)")
|
||||||
|
parser.add_argument('--whitelist', type=str, default="/etc/audit/suid_whitelist.json",
|
||||||
|
help="Fichier de whitelist (défaut: /etc/audit/suid_whitelist.json)")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
config = AuditConfig(
|
||||||
|
log_dir=args.log_dir,
|
||||||
|
whitelist_file=args.whitelist,
|
||||||
|
max_depth=args.max_depth,
|
||||||
|
check_temp_dirs=not args.no_temp,
|
||||||
|
check_orphans=not args.no_orphan,
|
||||||
|
compute_hashes=not args.no_hash
|
||||||
|
)
|
||||||
|
|
||||||
|
# Exécution de l'audit
|
||||||
|
auditor = SUIDGUIDAuditor(config)
|
||||||
|
exit_code = auditor.run()
|
||||||
|
sys.exit(exit_code)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
315
audit-suid.sh
Normal file
315
audit-suid.sh
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script d'audit des fichiers SUID et GUID
|
||||||
|
# Auteur: Zen6
|
||||||
|
# Date: $(date +%Y-%m-%d)
|
||||||
|
# Version: 1.0
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
LOG_DIR="/var/log/audit"
|
||||||
|
LOG_FILE="$LOG_DIR/suid_guid_audit_$(date +%Y%m%d_%H%M%S).log"
|
||||||
|
REPORT_FILE="$LOG_DIR/suid_guid_report_$(date +%Y%m%d_%H%M%S).txt"
|
||||||
|
ALERT_FILE="$LOG_DIR/suid_guid_alerts_$(date +%Y%m%d_%H%M%S).txt"
|
||||||
|
WHITELIST_FILE="/etc/audit/suid_whitelist.conf"
|
||||||
|
|
||||||
|
# Couleurs pour l'affichage
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Fonction de logging
|
||||||
|
log() {
|
||||||
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction d'affichage
|
||||||
|
print_header() {
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}$1${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Vérification des privilèges root
|
||||||
|
check_root() {
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo -e "${RED}Ce script doit être exécuté en tant que root${NC}"
|
||||||
|
echo "Utilisez: sudo $0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Création des répertoires nécessaires
|
||||||
|
setup_directories() {
|
||||||
|
if [[ ! -d "$LOG_DIR" ]]; then
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
chmod 750 "$LOG_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Création du fichier de whitelist par défaut
|
||||||
|
create_default_whitelist() {
|
||||||
|
if [[ ! -f "$WHITELIST_FILE" ]]; then
|
||||||
|
mkdir -p "$(dirname $WHITELIST_FILE)"
|
||||||
|
cat > "$WHITELIST_FILE" << EOF
|
||||||
|
# Liste blanche des binaires SUID/SGID légitimes
|
||||||
|
# Format: chemin/du/binaire [SUID|SGID|BOTH]
|
||||||
|
/bin/su SUID
|
||||||
|
/bin/ping SUID
|
||||||
|
/bin/mount SUID
|
||||||
|
/bin/umount SUID
|
||||||
|
/usr/bin/passwd SUID
|
||||||
|
/usr/bin/sudo SUID
|
||||||
|
/usr/bin/chsh SUID
|
||||||
|
/usr/bin/chfn SUID
|
||||||
|
/usr/bin/gpasswd SUID
|
||||||
|
/usr/bin/crontab SUID
|
||||||
|
/usr/bin/at SUID
|
||||||
|
/usr/bin/newgrp SUID
|
||||||
|
/usr/sbin/unix_chkpwd SUID
|
||||||
|
/usr/bin/wall SGID
|
||||||
|
/usr/bin/write SGID
|
||||||
|
/usr/bin/ssh-agent SGID
|
||||||
|
/usr/bin/locate SGID
|
||||||
|
/usr/bin/mlocate SGID
|
||||||
|
EOF
|
||||||
|
log "Fichier de whitelist créé: $WHITELIST_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction pour charger la whitelist
|
||||||
|
load_whitelist() {
|
||||||
|
declare -gA WHITELIST_SUID
|
||||||
|
declare -gA WHITELIST_SGID
|
||||||
|
|
||||||
|
if [[ -f "$WHITELIST_FILE" ]]; then
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
# Ignorer les commentaires et lignes vides
|
||||||
|
[[ "$line" =~ ^#.*$ ]] && continue
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
|
||||||
|
read -r path type <<< "$line"
|
||||||
|
if [[ -n "$path" ]]; then
|
||||||
|
case "$type" in
|
||||||
|
SUID) WHITELIST_SUID["$path"]=1 ;;
|
||||||
|
SGID) WHITELIST_SGID["$path"]=1 ;;
|
||||||
|
BOTH) WHITELIST_SUID["$path"]=1; WHITELIST_SGID["$path"]=1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done < "$WHITELIST_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction principale d'audit
|
||||||
|
audit_files() {
|
||||||
|
local tmp_file="/tmp/suid_audit_$$.tmp"
|
||||||
|
|
||||||
|
print_header "AUDIT DES FICHIERS SUID ET GUID"
|
||||||
|
log "Début de l'audit des fichiers SUID/SGID"
|
||||||
|
|
||||||
|
# Recherche des fichiers SUID
|
||||||
|
log "Recherche des fichiers SUID..."
|
||||||
|
find / -type f -perm -4000 2>/dev/null | while read -r file; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
local perms=$(ls -l "$file" | awk '{print $1}')
|
||||||
|
local owner=$(ls -l "$file" | awk '{print $3}')
|
||||||
|
local size=$(ls -lh "$file" | awk '{print $5}')
|
||||||
|
local mtime=$(stat -c %y "$file" 2>/dev/null | cut -d. -f1)
|
||||||
|
|
||||||
|
# Vérification dans la whitelist
|
||||||
|
if [[ -z "${WHITELIST_SUID[$file]}" ]]; then
|
||||||
|
echo "$file|SUID|$owner|$perms|$size|$mtime" >> "$tmp_file"
|
||||||
|
echo -e "${RED}[ALERTE SUID]${NC} $file (propriétaire: $owner, permissions: $perms)"
|
||||||
|
log "ALERTE SUID: $file (propriétaire: $owner)"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK SUID]${NC} $file (whitelisté)"
|
||||||
|
log "OK SUID: $file (whitelisté)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Recherche des fichiers SGID
|
||||||
|
log "Recherche des fichiers SGID..."
|
||||||
|
find / -type f -perm -2000 2>/dev/null | while read -r file; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
local perms=$(ls -l "$file" | awk '{print $1}')
|
||||||
|
local group=$(ls -l "$file" | awk '{print $4}')
|
||||||
|
local size=$(ls -lh "$file" | awk '{print $5}')
|
||||||
|
local mtime=$(stat -c %y "$file" 2>/dev/null | cut -d. -f1)
|
||||||
|
|
||||||
|
# Vérification dans la whitelist
|
||||||
|
if [[ -z "${WHITELIST_SGID[$file]}" ]]; then
|
||||||
|
echo "$file|SGID|$group|$perms|$size|$mtime" >> "$tmp_file"
|
||||||
|
echo -e "${RED}[ALERTE SGID]${NC} $file (groupe: $group, permissions: $perms)"
|
||||||
|
log "ALERTE SGID: $file (groupe: $group)"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK SGID]${NC} $file (whitelisté)"
|
||||||
|
log "OK SGID: $file (whitelisté)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Génération du rapport
|
||||||
|
if [[ -f "$tmp_file" ]]; then
|
||||||
|
generate_report "$tmp_file"
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
else
|
||||||
|
log "Aucune anomalie SUID/SGID détectée"
|
||||||
|
echo -e "${GREEN}Aucune anomalie SUID/SGID détectée${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Génération du rapport détaillé
|
||||||
|
generate_report() {
|
||||||
|
local tmp_file="$1"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "=========================================="
|
||||||
|
echo "RAPPORT D'AUDIT SUID/SGID"
|
||||||
|
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "Serveur: $(hostname)"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "ANOMALIES DÉTECTÉES:"
|
||||||
|
echo "--------------------"
|
||||||
|
|
||||||
|
local suid_count=0
|
||||||
|
local sgid_count=0
|
||||||
|
|
||||||
|
while IFS='|' read -r file type owner perms size mtime; do
|
||||||
|
if [[ "$type" == "SUID" ]]; then
|
||||||
|
((suid_count++))
|
||||||
|
echo ""
|
||||||
|
echo "[$suid_count] FICHIER SUID SUSPECT: $file"
|
||||||
|
echo " Propriétaire: $owner"
|
||||||
|
echo " Permissions: $perms"
|
||||||
|
echo " Taille: $size"
|
||||||
|
echo " Dernière modification: $mtime"
|
||||||
|
echo " Recommandation: Vérifier la légitimité de ce binaire"
|
||||||
|
elif [[ "$type" == "SGID" ]]; then
|
||||||
|
((sgid_count++))
|
||||||
|
echo ""
|
||||||
|
echo "[$sgid_count] FICHIER SGID SUSPECT: $file"
|
||||||
|
echo " Groupe: $owner"
|
||||||
|
echo " Permissions: $perms"
|
||||||
|
echo " Taille: $size"
|
||||||
|
echo " Dernière modification: $mtime"
|
||||||
|
echo " Recommandation: Vérifier la légitimité de ce binaire"
|
||||||
|
fi
|
||||||
|
done < "$tmp_file"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "STATISTIQUES:"
|
||||||
|
echo "-------------"
|
||||||
|
echo "Total SUID suspects: $suid_count"
|
||||||
|
echo "Total SGID suspects: $sgid_count"
|
||||||
|
echo ""
|
||||||
|
echo "RECOMMANDATIONS:"
|
||||||
|
echo "----------------"
|
||||||
|
echo "1. Examinez chaque binaire suspect manuellement"
|
||||||
|
echo "2. Supprimez les bits SUID/SGID si non nécessaires: chmod u-s <fichier>"
|
||||||
|
echo "3. Ajoutez les binaires légitimes à la whitelist: $WHITELIST_FILE"
|
||||||
|
echo "4. Vérifiez l'intégrité des binaires système"
|
||||||
|
echo "5. Surveillez les changements régulièrement"
|
||||||
|
|
||||||
|
} > "$REPORT_FILE"
|
||||||
|
|
||||||
|
# Création du fichier d'alertes (uniquement les anomalies)
|
||||||
|
grep -E "^\[ALERTE" "$LOG_FILE" > "$ALERT_FILE" 2>/dev/null || true
|
||||||
|
|
||||||
|
log "Rapport généré: $REPORT_FILE"
|
||||||
|
log "Alertes sauvegardées: $ALERT_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction de vérification supplémentaire (fichiers dans /tmp, /var/tmp)
|
||||||
|
check_temp_directories() {
|
||||||
|
print_header "VÉRIFICATION DES RÉPERTOIRES TEMPORAIRES"
|
||||||
|
log "Vérification des répertoires /tmp et /var/tmp..."
|
||||||
|
|
||||||
|
for dir in /tmp /var/tmp /dev/shm; do
|
||||||
|
if [[ -d "$dir" ]]; then
|
||||||
|
echo -e "${YELLOW}Vérification de $dir:${NC}"
|
||||||
|
find "$dir" -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | while read -r file; do
|
||||||
|
echo -e "${RED}[URGENT] Fichier suspect dans $dir: $file${NC}"
|
||||||
|
log "URGENT: Fichier SUID/SGID dans $dir: $file"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction de vérification des fichiers sans propriétaire
|
||||||
|
check_orphan_files() {
|
||||||
|
print_header "FICHIERS SUID/SGID SANS PROPRIÉTAIRE"
|
||||||
|
log "Recherche des fichiers SUID/SGID sans propriétaire valide..."
|
||||||
|
|
||||||
|
find / -type f \( -perm -4000 -o -perm -2000 \) -nouser -o -nogroup 2>/dev/null | while read -r file; do
|
||||||
|
echo -e "${RED}[ORPHELIN] $file${NC}"
|
||||||
|
log "ORPHELIN: $file"
|
||||||
|
echo "$file" >> "$ALERT_FILE"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction de vérification des hash MD5 pour les binaires système
|
||||||
|
check_system_binaries() {
|
||||||
|
print_header "VÉRIFICATION DES BINAIRES SYSTÈME"
|
||||||
|
log "Vérification des binaires système critiques..."
|
||||||
|
|
||||||
|
local system_bins=("/bin/su" "/bin/ping" "/usr/bin/sudo" "/usr/bin/passwd")
|
||||||
|
|
||||||
|
for bin in "${system_bins[@]}"; do
|
||||||
|
if [[ -f "$bin" ]]; then
|
||||||
|
local md5=$(md5sum "$bin" 2>/dev/null | awk '{print $1}')
|
||||||
|
log "Hash MD5 de $bin: $md5"
|
||||||
|
echo "$bin: $md5" >> "$LOG_DIR/system_hashes_$(date +%Y%m%d).txt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fonction principale
|
||||||
|
main() {
|
||||||
|
echo -e "${BLUE}"
|
||||||
|
cat << "EOF"
|
||||||
|
╔═══════════════════════════════════════╗
|
||||||
|
║ SUID/SGID Security Audit Tool ║
|
||||||
|
║ Linux Privilege Scanner ║
|
||||||
|
╚═══════════════════════════════════════╝
|
||||||
|
EOF
|
||||||
|
echo -e "${NC}"
|
||||||
|
|
||||||
|
check_root
|
||||||
|
setup_directories
|
||||||
|
create_default_whitelist
|
||||||
|
load_whitelist
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
audit_files
|
||||||
|
echo ""
|
||||||
|
check_temp_directories
|
||||||
|
echo ""
|
||||||
|
check_orphan_files
|
||||||
|
echo ""
|
||||||
|
check_system_binaries
|
||||||
|
|
||||||
|
print_header "RÉSUMÉ DE L'AUDIT"
|
||||||
|
echo -e "Log complet: ${GREEN}$LOG_FILE${NC}"
|
||||||
|
echo -e "Rapport détaillé: ${GREEN}$REPORT_FILE${NC}"
|
||||||
|
echo -e "Alertes: ${RED}$ALERT_FILE${NC}"
|
||||||
|
echo -e "Whitelist: ${YELLOW}$WHITELIST_FILE${NC}"
|
||||||
|
|
||||||
|
if [[ -s "$ALERT_FILE" ]]; then
|
||||||
|
echo -e "\n${RED}⚠️ ATTENTION: Des anomalies ont été détectées !${NC}"
|
||||||
|
echo "Consultez $ALERT_FILE pour plus de détails"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo -e "\n${GREEN}✓ Aucune anomalie détectée${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gestion des signaux
|
||||||
|
trap 'echo -e "\n${RED}Script interrompu par utilisateur${NC}"; exit 1' INT TERM
|
||||||
|
|
||||||
|
# Exécution
|
||||||
|
main "$@"
|
||||||
|
|
||||||
Reference in New Issue
Block a user