
Prova de conceito de exploit para CVE-2026-41651, uma escalada local de privilégios TOCTOU no PackageKit, com análise técnica, lógica de detecção e orientações de remediação.
Classificação: Artefato de Avaliação Purple Team
Uso autorizado apenas. Este documento e o script de teste que o acompanha destinam-se exclusivamente à validação interna de segurança em sistemas onde foi obtida autorização por escrito.

| Campo | Valor |
|---|---|
| ID CVE | CVE-2026-41651 |
| CWE | CWE-367 — Condição de Corrida Time-of-check Time-of-use (TOCTOU) |
| Componente | PackageKit (packagekitd, serviço D-Bus) |
| Afetado | PackageKit ≤ 1.3.4 |
| Corrigido em | PackageKit 1.3.5 |
| Impacto | Escalação Local de Privilégios → root |
| Vetor de Ataque | Local / D-Bus (sessão de usuário sem privilégios) |
| Divulgado | 2026-04-22 (Deutsche Telekom Red Team) |
| Aviso | GHSA-f55j-vvr9-69xv |
| Commit da correção | 76cfb675fb31acc3ad5595d4380bfff56d2a8697 |
PackageKit é uma camada de abstração D-Bus para gerenciamento de pacotes do sistema, presente por padrão em desktops baseados em GNOME em Debian, Ubuntu, Fedora, RHEL, SUSE e Arch Linux. Como ele media operações privilegiadas de pacotes em nome de usuários sem privilégios, uma falha em seu fluxo de autorização tem impacto de root em todo o sistema.
pk-transaction.c (pré-1.3.5) não aplicava uma proteção de estado na re-invocação de métodos de ação. Um cliente D-Bus poderia chamar InstallFiles (ou outros métodos de ação) múltiplas vezes no mesmo objeto de transação depois que ele já tivesse saído de PK_TRANSACTION_STATE_NEW.
Atacante (sem privilégios)
│
├─① CreateTransaction() → PackageKit retorna o caminho do objeto da transação (tid)
│
├─② InstallFiles(tid, FLAG_SIMULATE=4, [dummy.pkg])
│ PackageKit enfileira uma verificação de autorização polkit para dummy.pkg.
│ Nenhuma instalação ocorre ainda — SIMULATE significa apenas teste simulado.
│
├─③ InstallFiles(tid, FLAG_NONE=0, [payload.pkg]) ← janela TOCTOU
│ Re-invoca no mesmo tid antes que a autorização seja resolvida.
│ Versões vulneráveis sobrescrevem os parâmetros enfileirados com payload.pkg.
│
└─④ polkit concede autorização (usuário aprovou ou autorização automática)
packagekitd instala payload.pkg como root.
script postinst/post do payload: install -m 4755 /bin/bash /tmp/.suid_bash
Atacante executa /tmp/.suid_bash -p → shell root.
Os passos ② e ③ são enviados como chamadas D-Bus assíncronas não bloqueantes na mesma conexão e liberados em uma única escrita. As duas mensagens chegam ao packagekitd antes que ele possa processar ② e avançar a máquina de estados, deixando a janela TOCTOU aberta. A correção em 1.3.5 adiciona uma verificação explícita de estado que retorna PK_TRANSACTION_ERROR_INVALID_STATE em qualquer re-invocação após PK_TRANSACTION_STATE_NEW.
Arquivo: cve-2026-41651-purpleteam.py
Linguagem: Python 3
Dependências: python3-gi (introspecção GObject / bindings GLib/Gio)
Demonstra a explorabilidade do CVE-2026-41651 em um sistema de teste preparado para os seguintes fins:
| Fase | Ação |
|---|---|
| Configuração | Verifica /proc/mounts para selecionar um diretório de descarte com SUID/exec-capable, define umask(022), aplica chmod explícito em todos os artefatos de build e, em seguida, cria um pacote dummy e um pacote payload em /tmp |
| Exploração | Abre uma conexão D-Bus do sistema, cria uma transação PackageKit e dispara a corrida de duas chamadas |
| Payload | O script pós-instalação do pacote copia /bin/bash para <drop_dir>/.suid_bash com modo 04755, proprietário root. O diretório de descarte é resolvido em tempo de execução (veja Suporte a Ambientes Reforçados) |
| Escalação | Verifica periodicamente o binário SUID (timeout de 90 s) e, em seguida, executa execl nele com -p para obter um shell root |
| Limpeza | Remove os arquivos temporários .deb/.rpm na saída (sucesso ou falha) |
Sistemas reforçados por padrão podem bloquear a exploração em dois pontos independentes. O script lida com ambos automaticamente.
027 / 077)Um umask herdado pelo processo de 027 ou 077 faz com que mkdir() produza diretórios 750 ou 700. Tanto dpkg-deb quanto rpmbuild precisam percorrer toda a árvore de build — se qualquer diretório for ilegível, o build falha silenciosamente.
Correção aplicada: os.umask(0o022) é chamado na inicialização antes de qualquer arquivo ou diretório ser criado. Além disso, todo diretório e arquivo na árvore de build recebe um chmod explícito imediatamente após a criação (0o755 para diretórios e scripts, 0o644 para arquivos de dados), garantindo as permissões corretas independentemente do umask herdado.
nosuid / noexec em /tmp| Flag | Efeito na exploração |
|---|---|
nosuid | O kernel remove silenciosamente o bit SUID de qualquer arquivo armazenado nesse sistema de arquivos — o bash copiado nunca se torna root |
noexec | O binário SUID não pode ser executado de forma alguma |
Correção aplicada: Na inicialização, _find_suid_dir() lê /proc/mounts e testa diretórios candidatos em ordem de preferência até encontrar um cujo sistema de arquivos não tenha nenhuma das flags definidas. O caminho resolvido é então incorporado ao script pós-instalação do payload e usado para verificação e execução.