
Detector + PoC para vulnerabilidades de escrita no page-cache do Linux: Copy Fail (CVE-2026-31431) e Dirty Frag (CVE-2026-43284/43500). Apenas pesquisa de segurança autorizada.
Um detector unificado e harness de PoC para as famílias de vulnerabilidades Copy Fail e Dirty Frag de escrita no page-cache do Linux.``` ██████╗ ██╗██████╗ ████████╗██╗ ██╗███████╗ █████╗ ██╗██╗ ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗ ██╔╝██╔════╝██╔══██╗██║██║ ██║ ██║██║██████╔╝ ██║ ╚████╔╝ █████╗ ███████║██║██║ ██║ ██║██║██╔══██╗ ██║ ╚██╔╝ ██╔══╝ ██╔══██║██║██║ ██████╔╝██║██║ ██║ ██║ ██║ ██║ ██║ ██║██║███████╗ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝
DIRTYFAIL é uma ferramenta C pequena e bem documentada para pesquisadores de segurança.
Ela detecta se um host Linux é vulnerável às três CVEs desta
família e — com confirmação explícita e digitada — executa uma prova de
conceito real que coloca o chamador em um shell root em um
sistema vulnerável.
| CVE / variant | Name | DIRTYFAIL coverage |
|---|---|---|
| **CVE-2026-31431** | Copy Fail (algif_aead `authencesn` — escrita no page-cache) | Detecção + PoC completo |
| **CVE-2026-43284 v4** | Dirty Frag — escrita no page-cache do xfrm-ESP IPv4 | Detecção + PoC completo |
| **CVE-2026-43284 v6** | Dirty Frag — escrita no page-cache do xfrm-ESP IPv6 (`esp6`) | Detecção + PoC completo |
| **CVE-2026-43500** | Dirty Frag — escrita no page-cache do RxRPC | Detecção + PoC completo |
| Variante GCM do Copy Fail | Escrita no page-cache do xfrm-ESP `rfc4106(gcm(aes))` | Detecção + PoC completo |
**Modos bônus:**
- **`--scan --active`** — sondas ativas de sentinela-STORE. O `--scan` padrão
relata as pré-condições por CVE (kernel, módulos, estado do LSM) além de uma
sonda ativa da primitiva do Copy Fail. Adicionar `--active` estende
a sonda STORE de arquivo sentinela para todas as outras quatro primitivas (ESP v4,
ESP v6, RxRPC, GCM): cada uma dispara o gatilho do kernel contra um
sentinela em `/tmp` e relata VULNERABLE somente se os bytes de marcação
realmente chegarem. Esta é a única maneira de distinguir um kernel
com patch backportado (pré-condições dizem vulnerável, mas a sonda diz intacto) de um
sem patch sem executar o exploit completo. `/etc/passwd` nunca é
tocado. Auto-calibra o deslocamento de STORE V6 por build do kernel.
- **`--exploit-backdoor`** — backdoor persistente de uid-0: sobrescrita
com comprimento correspondente de uma linha `nologin`/`false`/`sync` em `/etc/passwd` com
`dirtyfail::0:0:<pad>:/:/bin/bash`. Sobrevive à saída do shell até a página
ser despejada. Estado armazenado em `/var/tmp/.dirtyfail.state` para
`--cleanup-backdoor`. O nome de usuário `dirtyfail` é deliberadamente
combinado com este projeto para que seja instantaneamente identificável em qualquer
auditoria — altere `NEW_USER` em `src/backdoor.c` se precisar de um
identificador diferente para um engajamento autorizado de red team.
- **Bypass do AppArmor** — contorna a política
`apparmor_restrict_unprivileged_userns=1` do Ubuntu por meio de um re-exec
`change_onexec("crun")` de salto único em um perfil não confinado que
mantém as capacidades de userns. Cada modo de exploit lida com isso
internamente via um fork: o pai permanece no namespace init, o filho faz
a dança do bypass, o pai lê o page cache global e executa `su` para
obter root REAL no init namespace. A flag legada `--aa-bypass` ainda existe para
depurar a mecânica do bypass isoladamente. Veja [§8.5 Arquitetura](#85-architecture-outerinner-fork-based-bypass).
## Verificado funcionando em
O DIRTYFAIL foi **validado empiricamente de ponta a ponta** em múltiplas
distros e versões de kernel. A matriz abaixo reflete os resultados de teste por modo
da execução de cada modo `--exploit-*` em uma instalação limpa
de cada distro.
| Distro | Kernel | LSM | Copy Fail | xfrm-ESP v4 | xfrm-ESP v6 | RxRPC | GCM | Backdoor | SU shellcode |
|---|---|---|---|---|---|---|---|---|---|
| Ubuntu 24.04 LTS | `6.8.0-111-generic` | AppArmor | 🛡² | ✅ | ✅ | ✅ | ✅¹ | ✅¹ | (não testado) |
| Debian 13.4 | `6.12.86+deb13` | nenhum | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡⁵ |
| AlmaLinux 10.1 | `6.12.0-124.8.1.el10_1` | SELinux | ✅ | ✅ | ✅ | ⏭³ | ✅ | ✅ | ✅ |
| Fedora 44 (Server) | `6.19.10-300.fc44` | SELinux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ubuntu 26.04 LTS | `7.0.0-15-generic` | AppArmor (reforçado) | 🛡 | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁵ |
**Legenda:** ✅ exploit chegou e produziu root real no init namespace · 🛡 mitigado — o exploit não consegue alcançar o bug do kernel (kernel corrigido OU LSM bloqueia o caminho não privilegiado) · ⏭ não aplicável (pré-condição ausente)
### Validação de sonda ativa (`--scan --active`)
A flag `--active` adiciona uma sonda STORE de arquivo sentinela por CVE durante
a detecção. Validamos as saídas da sonda nas mesmas 4 distros
acima (Debian, Fedora, AlmaLinux, Ubuntu 26.04) — a matriz abaixo
mostra o veredito da sonda por modo e corresponde ao ground-truth
do exploit completo, um a um:
| Distro | Sonda Copy Fail | Sonda ESP v4 | Sonda ESP v6 | Sonda RxRPC | Sonda GCM |
|---|---|---|---|---|---|
| Debian 13.4 | intacto 🛡 | intacto 🛡 | intacto 🛡 | intacto 🛡 | intacto 🛡 |
| Fedora 44 | marcador @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | alteração de byte ✅ | sentinel[0] 0x41→0x27 ✅ |
| AlmaLinux 10.1 | marcador @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | pré-condições ⏭ | sentinela alterada ✅ |
| Ubuntu 26.04 | intacto 🛡 | bloqueado por LSM 🛡 | bloqueado por LSM 🛡 | bloqueado por LSM 🛡 | bloqueado por LSM 🛡 |
O offset de aterrissagem do STORE da sonda V6 (8 no Fedora e no Alma) corresponde ao
`V6_STORE_SHIFT` empírico que `calibrate_v6_shift()` descobre em
tempo de execução — confirmando que a auto-calibração substitui corretamente a
constante anteriormente hard-coded em diferentes builds do kernel.
¹ GCM e Backdoor exigem que `algif_aead` seja carregável. O Ubuntu 24.04
traz `/etc/modprobe.d/disable-algif_aead.conf` que o coloca na blacklist como
mitigação do Copy Fail. Com a blacklist removida (por exemplo, em um kernel
anterior à mitigação), ambos os modos funcionam de ponta a ponta.
² O caminho algif_aead do Copy Fail é mitigado pela blacklist do modprobe;
a primitiva CVE subjacente no kernel é a mesma, esteja
`authencesn` acessível ou não. xfrm-ESP, RxRPC e a variante GCM todos
chegam ao mesmo kernel porque não passam por algif_aead.
³ O pacote `kernel-modules-extra` do AlmaLinux 10 não é instalado por
padrão em uma instalação Minimal, então `rxrpc.ko` está ausente no disco.
Instalar `kernel-modules-extra-$(uname -r)` do EPEL ou do repositório extras do AlmaLinux
traz o módulo de volta; em uma instalação mínima padrão, o RxRPC fica
inacessível.
⁴ **O Ubuntu 26.04 LTS bloqueia de forma abrangente a exploração não privilegiada.**
O kernel fornecido `7.0.0-15.15` (lançado em 2026-04-22) **é anterior ao
patch mainline `f4c50a4034e6` (mesclado em 2026-05-07) em ~2 semanas** — então
o bug AINDA está presente no kernel. A defesa do Ubuntu é
**defesa em profundidade via reforço do AppArmor**, não um patch de kernel:
- `apparmor_restrict_unprivileged_userns=1` está habilitado por padrão.
- Ao `unshare(CLONE_NEWUSER)`, a aplicação do AppArmor em nível de kernel
faz transição automática de QUALQUER perfil (incluindo os marcados com `(unconfined)`
como `crun`, `chrome` e o `unconfined` padrão) para um
subperfil `<profile>//&unprivileged_userns (mixed)` que possui
`audit deny capability`. O uid 0 dentro do novo userns não recebe caps.
- `change_onexec` para um perfil diferente não ajuda — até o
perfil `crun` (que tem permissão explícita `userns,` e
`flags=(unconfined)`) faz transição automática no unshare. Verificado via
`aa-exec -p crun bash -c 'unshare -U -n cat /proc/self/attr/current'`
→ `crun//&unprivileged_userns (mixed)`.
- `newuidmap`/`newgidmap` (setuid root) escrevem com sucesso o uid_map,
mas `setresuid(0)` então é bem-sucedido enquanto `ioctl(SIOCSIFFLAGS)` e
todas as outras syscalls controladas por CAP_NET_ADMIN retornam EPERM porque a
negação de capability é por namespace, não por uid.