- SUID forza l'EUID al proprietario del file al momento dell'esecuzione - se il proprietario è root, ogni utente che lo esegue ottiene EUID=0
- Un interprete con SUID root (python3, perl, bash) è escalation immediata: nessuna vulnerabilità da sfruttare, nessun exploit da compilare
find / -perm -4000 -type f 2>/dev/nullin 30 secondi elenca tutto quello che conta- Detection: baseline snapshot dei SUID in CI/CD + auditd rule su
execveconeuid=0eauid!=unset
▶ $ history
Sono le 02:41. Il SIEM ha alzato un flag su prod-api-03: processo root con parent python3, nessun deploy in corso, nessuna maintenance window schedulata. Il processo è già terminato quando apro il ticket. Non c'è output, non c'è file scritto. Solo un'esecuzione anomala durata undici secondi.
Undici secondi sono più che sufficienti.

Come funziona il bit SUID#
Il bit SUID (Set User ID) è un meccanismo del kernel Linux che modifica il comportamento dell'execve(): quando un eseguibile ha il bit s impostato e il proprietario è root, il kernel eleva l'EUID (Effective User ID) a 0 per tutta la durata dell'esecuzione, indipendentemente da chi ha lanciato il processo.
La distinzione tra RUID e EUID è il punto critico. RUID è chi sei davvero - l'utente che ha fatto login. EUID è chi sei in questo momento per il kernel quando verifica i permessi. Quasi tutto il permission model Linux usa EUID: apertura di file, bind su porte privilegiate, modifica di processi altrui.
execve("/usr/bin/python3")
│
▼
kernel controlla SUID bit sul file
│
├── proprietario: root (uid=0)
├── SUID bit: attivo
│
▼
EUID processo = 0 (anche se RUID = 1001)L'unica differenza tra un interprete normale e uno con SUID root è quel bit. Nessuna vulnerabilità, nessun buffer overflow. Solo un flag in mezzo ai permessi che la maggior parte dei tool di monitoring non controlla in real time.
Il primo segnale#
Il processo nel SIEM è già sparito, ma auditd gira su tutte le macchine di produzione. La prima query è sugli eventi execve con euid=0 nelle ultime quattro ore:
ausearch -m execve -k suid_exec --start 02:00:00 --end 03:00:00 -i
# output simulatotype=SYSCALL msg=audit(1711155723.441:8847): arch=c000003e syscall=59
success=yes exit=0 a0=... a1=... a2=... ppid=31204 pid=31205
uid=1001 gid=1001 euid=0 suid=0 fsuid=0 egid=1001 sgid=1001
fsgid=1001 tty=(none) ses=47 comm="sh" exe="/bin/sh"
subj=unconfined_u:... key="suid_exec"Il campo che conta: uid=1001 euid=0. Un utente non privilegiato (uid=1001, l'account di servizio deploy) ha eseguito qualcosa che gli ha dato EUID root. Il binario eseguito è /bin/sh. Il parent process era python3.
# recupero il parent PID per contestualizzare
ausearch -m execve -p 31204 --start 02:00:00 -i
# output simulatotype=SYSCALL ... comm="python3" exe="/usr/bin/python3"
uid=1001 gid=1001 euid=0 .../usr/bin/python3 eseguito con EUID=0 da uid=1001. Questo è il punto di ingresso.
La traccia#
stat /usr/bin/python3
# output simulato File: /usr/bin/python3
Size: 5479736
Access: (4755/-rwsr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)4755. Il 4 davanti è il SUID bit. -rwsr-xr-x: la s al posto della x nel gruppo owner. Qualcuno ha eseguito chmod u+s /usr/bin/python3 su questo server.
Per capire quando è successo:
stat --format="%n %y %z" /usr/bin/python3
# output simulato/usr/bin/python3 2026-03-22 23:14:07.341 +0000 (mtime)
2026-03-22 23:14:07.341 +0000 (ctime)Le 23:14 di ieri sera. Undici ore prima dell'alert. Cerco nei log chi era connesso al server in quella finestra:
ausearch -m USER_AUTH --start 23:00:00 --end 23:30:00 -i | grep "uid=1001\|uid=0"
# output simulatotype=USER_AUTH msg=audit(1711151647.002:7123): pid=28941 uid=0
acct="ops-user" exe="/usr/bin/sudo" ... res=successops-user ha usato sudo alle 23:14. Probabilmente stava configurando qualcosa. Il cambio di permessi su python3 è quasi certamente stato un incidente: volevamo chmod 755 e abbiamo scritto chmod 4755. O abbiamo lanciato chmod u+s invece di un altro comando.
Il punto non è che qualcuno ha voluto sabotare il server. Il punto è che un errore di digitazione di dieci caratteri su un server di produzione ha lasciato una backdoor root accessibile a qualunque processo che potesse chiamare python3.
Il pivot - la ricostruzione dell'exploit#
Dall'audit log dell'attaccante (o di chi ha scoperto l'apertura e l'ha sfruttata):
# dal punto di vista dell'utente deploy (uid=1001)
id
# uid=1001(deploy) gid=1001(deploy) groups=1001(deploy)
python3 -c "import os; os.execl('/bin/sh', 'sh', '-p')"
id
# uid=1001(deploy) gid=1001(deploy) euid=0(root) groups=1001(deploy)Il flag -p di /bin/sh disabilita il reset automatico dell'EUID: senza -p, alcune shell moderne abbassano volontariamente l'EUID al RUID per sicurezza. Con -p, la shell mantiene i privilegi elevati.
# verifica dal /proc per chi non si fida dei comandi utente
cat /proc/self/status | grep -E "^[UG]id"
# output simulato
Uid: 1001 0 0 0
Gid: 1001 1001 1001 1001A questo punto l'attaccante ha EUID=0. Può leggere /etc/shadow, modificare /etc/sudoers, scrivere authorized_keys in /root/.ssh/, installare un cron job root, estrarre segreti da /proc/PID/environ di qualunque processo.
Diagramma - scenario e kill chain#
graph TD
A[ops-user sudo session] -->|chmod 4755 typo| B[python3 SUID root]
B --> C{Attacco}
C -->|uid=1001 deploy account| D[python3 -c os.execl sh -p]
D --> E[shell EUID=0]
E --> F[shadow read]
E --> G[cron root install]
E --> H[authorized_keys root]
sequenceDiagram
participant U as ops-user
participant S as prod-api-03
participant A as deploy uid=1001
participant SIEM
U->>S: chmod 4755 python3 typo
Note over S: SUID bit attivo su python3
A->>S: python3 os.execl sh -p
S-->>A: shell con EUID=0
A->>S: cat /etc/shadow
A->>S: write root authorized_keys
S->>SIEM: execve euid=0 uid=1001
Note over SIEM: unprivileged user running as root
Tabella IoC#
| Tipo | Valore | Contesto | MITRE ATT&CK |
|---|---|---|---|
| Tecnica | SUID bit su interprete | /usr/bin/python3 con chmod 4755 | T1548.001 |
| Evento | execve con uid!=0 e euid=0 | auditd log prod-api-03 | T1548.001 |
| Comando | python3 -c "import os; os.execl('/bin/sh', 'sh', '-p')" | escalation da deploy account | T1059.006 |
| Artefatto | mtime /usr/bin/python3 2026-03-22T23:14:07Z | cambio permessi non schedulato | T1222.002 |
| Account | uid=1001 (deploy) con euid=0 | session 47, tty=none | T1078.003 |
Mitigazione#
Rimozione immediata:
# verifica lo stato attuale
stat --format="%A %n" /usr/bin/python3
# rimozione SUID
chmod u-s /usr/bin/python3
# verifica
stat --format="%A %n" /usr/bin/python3
# -rwxr-xr-x /usr/bin/python3Baseline e monitoring continuo:
# snapshot dei SUID in CI/CD o al boot
find / -perm -4000 -type f 2>/dev/null | sort > /var/lib/suid-baseline.txt
# confronto periodico (cron o systemd timer)
find / -perm -4000 -type f 2>/dev/null | sort | diff /var/lib/suid-baseline.txt -Regola auditd per SUID exec:
# /etc/audit/rules.d/suid.rules
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=unset -k suid_exec
-a always,exit -F arch=b32 -S execve -F euid=0 -F auid!=unset -k suid_exec
# applica senza reboot
auditctl -R /etc/audit/rules.d/suid.rulesChecklist di prevenzione:
- Verificare SUID in pipeline di deploy:
find / -perm -4000deve matchare la baseline -
inotifywait -m /usr/bin /usr/local/bin -e attribper alert real-time su cambio permessi - Separare account di deploy da account operativi -
ops-usernon dovrebbe avere accesso diretto aprod-api-03 - Per binari che richiedono privilegi elevati valutare Linux capabilities (
cap_net_bind_service,cap_dac_read_search) invece del SUID: scope più ristretto, stessa funzionalità - Se stai usando container:
--no-new-privilegesin Docker,allowPrivilegeEscalation: falsein Kubernetes securityContext
exit 0#
Undici ore tra il chmod e l'exploit. Su un sistema senza auditd, quella finestra potrebbe non avere un log. Su un sistema senza baseline dei SUID, quel bit potrebbe rimanere per mesi.
La difficoltà tecnica di questa escalation è zero. Non serve un exploit, non serve un CVE, non serve nemmeno sapere cosa si sta facendo: python3 -c "import os; os.execl('/bin/sh', 'sh', '-p')" è su ogni cheatsheet pubblico. Il valore del SUID bit non è nella complessità - è nella normalità. È un file system che ha un permesso che sembra ragionevole finché non lo è.
Il punto non è "non fare typo". Il punto è che un sistema ben difeso dovrebbe rilevare questo cambio in meno di un minuto, non a posteriori da un alert SIEM undici ore dopo.
Concetti correlati: iam · linux permissions
MITRE ATT&CK: T1548.001 - Setuid and Setgid


