-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsystemd-surface-audit.sh
More file actions
executable file
·170 lines (155 loc) · 9.79 KB
/
Copy pathsystemd-surface-audit.sh
File metadata and controls
executable file
·170 lines (155 loc) · 9.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env bash
# Auditoria genérica de superfície de ataque IPC no systemd, complementar às
# auditorias pós-fato específicas de cada CVE neste repo. Cobre duas frentes:
# 1. CVEs conhecidas nos pacotes instalados (via arch-audit, se disponível)
# 2. Sockets ativos de famílias "opt-in ligado por padrão" que costumam não
# ser usadas em desktop pessoal (VM/container, sysext, storage, ssh local)
#
# O raciocínio por trás da Frente 2, em uma frase: systemd socket-ativa vários
# mini-daemons OPCIONAIS por padrão, mesmo que você nunca tenha usado a
# funcionalidade deles — o socket existe e escuta desde o boot, pronto pra
# subir o processo (normalmente como root) assim que algo conectar nele. Cada
# família abaixo tem seu próprio "sinal de uso real"; a ausência desse sinal
# é o que torna `mask --now` seguro de aplicar sem quebrar nada.
#
# Ver docs/systemd-attack-surface-reduction.md para a metodologia completa.
# Somente leitura — não altera nada. Requer: systemd, pacman.
set -uo pipefail
section() { printf '\n=== %s ===\n' "$1"; }
section "Frente 1: CVEs conhecidas em pacotes instalados (arch-audit)"
if command -v arch-audit >/dev/null 2>&1; then
arch-audit
else
echo "arch-audit não instalado. Rode: sudo pacman -S arch-audit"
fi
section "Frente 2: todos os sockets ativos agora"
systemctl list-sockets --no-pager 2>&1
section "SocketMode de cada socket carregado (0666/0777 = qualquer usuário local conecta)"
for u in $(systemctl list-sockets --no-pager --no-legend --all 2>/dev/null | awk '{print $2}' | sort -u); do
mode=$(systemctl cat "$u" 2>/dev/null | grep -i '^SocketMode=' | tail -1)
[ -n "$mode" ] && echo "$u: $mode"
done
section "Família VM/container (machined + importd) — em uso?"
echo "Por que auditar: systemd-machined é o daemon por trás de machinectl/nspawn,"
echo "roda como root, e teve pelo menos um CVE de escalação de privilégio via"
echo "IPC (ver ../CVE-2026-4105-systemd-machined/). Socket-ativado por padrão"
echo "mesmo que você nunca tenha rodado machinectl manualmente."
systemctl is-active systemd-machined.socket systemd-importd.socket 2>&1
echo "Máquinas registradas / imagens importadas (esperado vazio se não usa):"
ls /run/systemd/machine/ 2>/dev/null
ls /var/lib/machines/ 2>/dev/null || echo "(sem permissão de leitura — rode como root para checar de fato, ou aceite ausência de evidência de uso)"
section "Família system extensions (sysext/confext) — em uso?"
echo "Por que auditar: mecanismo pra sobrepor /usr ou /etc em runtime via imagem"
echo "montada (pensado pra imutabilidade tipo NixOS/Fedora Silverblue). Se você"
echo "não usa esse modelo, o socket ainda existe escutando por padrão."
systemctl is-active systemd-sysext.socket 2>&1
ls -la /etc/extensions /var/lib/extensions 2>&1
section "Família storage/imagem de disco (varlink) — em uso?"
echo "Por que auditar: expõe operações privilegiadas de disco (dissecar imagem,"
echo "montar) via varlink pra quem chamar systemd-dissect/portablectl."
systemctl is-active systemd-storage-block.socket systemd-storage-fs.socket 2>&1
echo "Se você não usa systemd-dissect/portablectl/build de imagem de sistema, isso é candidato a mask."
section "SSH local sem rede (systemd-ssh-generator, systemd >=256) — em uso?"
echo "Por que auditar: cria um socket AF_UNIX/AF_VSOCK que aceita autenticação"
echo "SSH local — pensado pra acessar containers/VMs sem precisar de rede, mas"
echo "é gerado INCONDICIONALMENTE, mesmo em máquinas sem nenhum container."
systemctl is-active sshd-unix-local.socket 2>&1
echo "Gerado incondicionalmente a cada boot desde systemd 256, mesmo com sshd.service desabilitado."
echo "Se você não usa SSH via AF_UNIX/AF_VSOCK para containers/VMs locais, é candidato a mask."
section "Autostart de sessão (.desktop -> systemd --user, via uwsm/wayland-session-xdg-autostart) — em uso?"
echo "Se a sessão gráfica usa uwsm (ou qualquer session manager que amarre"
echo "xdg-desktop-autostart.target ao ciclo de vida da sessão), TODO .desktop em"
echo "/etc/xdg/autostart e ~/.config/autostart vira 'systemctl --user' unit sozinho"
echo "a cada login -- inclusive um .desktop malicioso plantado por algo que já"
echo "rode como você, sem precisar de cron nem edição de .bashrc."
systemctl --user is-enabled xdg-desktop-autostart.target 2>&1
echo "Units geradas agora a partir de autostart (esperado: só o que você reconhece):"
systemctl --user list-units 'app-*' --all --no-legend 2>/dev/null
echo "Se não quer que NENHUM .desktop vire unit automaticamente (recomendado se"
echo "você não depende de autostart de sessão pra nada crítico, ex.: gnome-keyring):"
echo " systemctl --user mask xdg-desktop-autostart.target"
section "Portais de desktop (xdg-desktop-portal e afins) — flatpak/snap em uso?"
if command -v flatpak >/dev/null 2>&1; then
echo "flatpak instalado -- xdg-document-portal provavelmente em uso de verdade, não mascarar sem checar."
else
echo "flatpak não instalado -- xdg-document-portal.service é candidato certo a mask (é específico de sandbox Flatpak/Snap)."
fi
systemctl --user is-enabled xdg-document-portal.service xdg-desktop-portal.service \
xdg-desktop-portal-gtk.service xdg-desktop-portal-hyprland.service \
xdg-permission-store.service 2>&1
echo "ATENÇÃO antes de mascarar o resto do stack (xdg-desktop-portal/-gtk/-hyprland/permission-store):"
echo "checar 'Required By' de cada app instalado -- diálogo nativo de abrir/salvar"
echo "arquivo em apps Electron/GTK sandboxed e compartilhamento de tela em chamada de"
echo "vídeo no navegador dependem disso. Ex.: pacman -Qi xdg-desktop-portal | grep 'Required By'"
section "NSS/identidade de usuário (userdbd) — NÃO mascarar sem checar isto primeiro"
systemctl is-active systemd-userdbd.socket 2>&1
echo "Módulo 'systemd' referenciado no NSS (se aparecer abaixo, está em uso interno -- não mascarar):"
grep systemd /etc/nsswitch.conf 2>&1
section "As mesmas famílias existem duplicadas na instância --user (não é privesc, mas é superfície)"
echo "A instância 'systemctl --user' roda seus próprios geradores e cria os mesmos"
echo "sockets em paralelo sob /run/user/\$UID/systemd/... -- mascarar a versão de"
echo "sistema NÃO mascara a versão --user. Diferença de risco: o processo do outro"
echo "lado aqui roda como VOCÊ, não como root, e o diretório /run/user/\$UID é 0700"
echo "(só o dono acessa) -- não é escalação de privilégio, mas ainda é superfície"
echo "desnecessária que um processo malicioso já rodando como você poderia abusar."
echo "Ver docs/systemd-attack-surface-reduction.md para a tabela completa de diferenças."
echo
systemctl --user list-sockets --no-pager --all 2>&1 | grep -iE "machined|importd|sysext|storage|ssh" || echo "(nenhuma das famílias rastreadas presente na instância --user)"
section "Verificação cruzada: mascarado no systemd == realmente fora do kernel?"
echo "systemctl pode dizer 'masked' e a unit ainda ter deixado um socket vivo (foi o"
echo "que aconteceu com machined nesta auditoria: mask sem --now não parou o que já"
echo "estava ativo). A prova real é o que o kernel mostra em 'ss', não o que o"
echo "systemd reporta sobre si mesmo. Cruza os dois pra cada unit da família:"
echo
ss_output=$(ss -l 2>&1)
ss_status=$?
if [ "$ss_status" -ne 0 ]; then
echo "ERRO: 'ss -l' falhou (status $ss_status) -- resultado abaixo não é confiável."
fi
for u in systemd-machined.socket systemd-importd.socket systemd-sysext.socket \
systemd-storage-block.socket systemd-storage-fs.socket sshd-unix-local.socket; do
state=$(systemctl show "$u" -p ActiveState --value 2>/dev/null)
path=$(systemctl cat "$u" 2>/dev/null | grep -i '^ListenStream=' | tail -1 | cut -d= -f2-)
if [ "$ss_status" -ne 0 ]; then
live="INDETERMINADO (ss falhou)"
elif [ -z "$path" ]; then
live="sem ListenStream= (não é socket de path, pular)"
elif printf '%s\n' "$ss_output" | grep -qF "$path"; then
live="AINDA ESCUTANDO no kernel"
else
live="não encontrado em ss"
fi
printf '%-32s systemctl=%-10s kernel(ss)=%s\n' "$u" "$state" "$live"
done
echo
echo "-- mesma checagem para a instância --user --"
for u in systemd-machined.socket systemd-importd.socket systemd-storage-fs.socket; do
state=$(systemctl --user show "$u" -p ActiveState --value 2>/dev/null)
path=$(systemctl --user cat "$u" 2>/dev/null | grep -i '^ListenStream=' | tail -1 | cut -d= -f2-)
if [ "$ss_status" -ne 0 ]; then
live="INDETERMINADO (ss falhou)"
elif [ -z "$path" ]; then
live="sem ListenStream= (não é socket de path, pular)"
elif printf '%s\n' "$ss_output" | grep -qF "$path"; then
live="AINDA ESCUTANDO no kernel"
else
live="não encontrado em ss"
fi
printf '%-32s systemctl--user=%-10s kernel(ss)=%s\n' "$u" "$state" "$live"
done
echo
echo "Se 'systemctl' disser inactive/masked mas 'kernel(ss)' disser 'AINDA ESCUTANDO',"
echo "o mask não pegou de verdade -- rode: sudo systemctl mask --now <unit>"
echo "(ou, pra instância --user, sem sudo: systemctl --user mask --now <unit>)"
echo
echo "Nota sobre falsos positivos ao ler 'ss' manualmente: sockets de apps de sessão"
echo "(ex.: terminal, navegador, barra de status) também aparecem em 'ss -pl' e são"
echo "normais -- eles pertencem ao SEU usuário (uid 1000) e ficam sob /run/user/1000/"
echo "ou em namespace abstrato ('@...'). O que importa auditar é especificamente os"
echo "sockets de sistema (donos root, sob /run/systemd/, /run/dbus/) que aparecem em"
echo "'systemctl list-sockets' -- não qualquer socket unix que qualquer app abrir."
section "Fim"
echo "Para qualquer item marcado 'candidato a mask' sem uso comprovado:"
echo " sudo systemctl mask --now <unit>"
echo "mask sozinho NÃO para uma unit já ativa -- sempre use --now ou confirme"
echo "'Active: inactive (dead)' depois, não só 'Loaded: masked'."