Vulnerabilidades que o Rust Não Captura: Lições do Auditoria do uutils

Vulnerabilidades que o Rust Não Captura: Lições do Auditoria do uutils

Uma análise aprofundada das vulnerabilidades descobertas no uutils, a implementação em Rust do GNU coreutils, revelando onde a segurança do Rust pode falhar e como evitá-las.

MundiX News·12 de maio de 2026·15 min de leitura·👁 4 views

Em abril de 2026, a Canonical divulgou 44 CVEs na uutils, a implementação em Rust do GNU coreutils, que vem por padrão a partir da versão 25.10. A maioria das vulnerabilidades foi descoberta durante uma auditoria externa realizada antes do lançamento do 26.04 LTS. Ao analisar essa lista, é possível extrair lições valiosas. Notavelmente, todos esses bugs estavam na base de código Rust, escrita por desenvolvedores experientes, e nenhum deles foi detectado pelo verificador de empréstimos (borrow checker), pelos lints do clippy ou pelo cargo audit.

Esta análise não visa criticar a equipe de desenvolvimento da uutils, mas sim agradecer pela publicação dos resultados da auditoria com detalhes que permitem a todos nós aprendermos. Se você desenvolve código de sistema em Rust, este artigo oferece uma análise concisa sobre os limites atuais da segurança proporcionada pelo Rust. É crucial não confiar cegamente no caminho entre duas chamadas de sistema. Este é o maior conjunto de bugs encontrados na auditoria e a razão pela qual cp, mv e rm ainda são implementados pela GNU no Ubuntu 26.04 LTS. O padrão é sempre o mesmo: uma chamada de sistema é usada para verificar informações sobre um caminho, e outra é usada para executar uma ação nesse mesmo caminho. Entre essas duas chamadas, um atacante com acesso de escrita na pasta pai pode substituir um componente do caminho por um link simbólico. Na segunda chamada, o kernel resolverá o caminho novamente do zero, e a ação privilegiada será executada no alvo escolhido pelo atacante. A biblioteca padrão do Rust facilita a ocorrência desse erro. APIs ergonômicas como fs::metadata, File::create, fs::remove_file e fs::set_permissions resolvem o caminho a cada vez, em vez de operar a partir de um descritor de arquivo. Para um programa comum, isso é aceitável, mas para ferramentas com privilégios elevados que precisam ser protegidas contra atacantes locais, é preciso ter cautela. Por exemplo, o CVE-2026-35355 demonstra um bug simplificado onde a remoção de um arquivo seguida pela sua recriação com File::create permite a inserção de um link simbólico, como /etc/shadow, que seria sobrescrito. A correção envolve o uso de OpenOptions::create_new(true), que garante que o arquivo seja novo e não permita links simbólicos pendentes.

A regra fundamental aqui é: ao trabalhar com caminhos em Rust, lembre-se que &Path é um valor, mas para o kernel, é apenas um nome que pode mudar entre chamadas de sistema. É essencial vincular operações a um descritor de arquivo. O create_new() ajuda apenas na criação de novos arquivos. Para outras operações, abra a pasta pai uma vez e trabalhe relativamente a esse descritor. Se você executa uma ação duas vezes no mesmo caminho, assuma a existência de um bug TOCTOU (Time Of Check To Time Of Use) até prova em contrário. Outra questão importante é definir permissões durante a criação, não depois. Criar um diretório com permissões padrão e depois alterá-las deixa uma janela de tempo onde outros usuários podem acessá-lo. Utilize OpenOptions::mode() e DirBuilderExt::mode() para garantir que arquivos e pastas sejam criados com as permissões desejadas. A igualdade de strings em caminhos não é equivalente à identidade no sistema de arquivos. Uma verificação inicial de --preserve-root em chmod que se baseava em comparações de strings podia ser contornada por caminhos que resolvessem para / mas não fossem literalmente /, como /../ ou links simbólicos. A correção envolve o uso de fs::canonicalize antes de comparar caminhos para resolver .., . e links simbólicos em caminhos absolutos reais. Para comparar dois caminhos arbitrários em busca de identidade no sistema de arquivos, é necessário abri-los e comparar seus pares (dev, inode), como faz o GNU coreutils. O bug CVE-2026-35363, onde rm . e rm .. falhavam, mas rm ./ e rm ./// eram aceitos e removiam o diretório atual, exemplifica essa falha de lógica.

É vital permanecer dentro dos limites de bytes nas fronteiras do Unix. String e &str em Rust são sempre UTF-8, o que é ótimo na maioria dos casos. No entanto, caminhos, variáveis de ambiente e entradas Unix podem estar em um estado confuso de bytes. Ao cruzar essa fronteira, um programa Rust tem três opções: conversão com perda (from_utf8_lossy), que substitui bytes inválidos por U+FFFD sem notificação; conversão rigorosa (unwrap ou ?), que causa falhas; ou permanecer com bytes usando OsStr ou &[u8]. A auditoria encontrou bugs nas duas primeiras categorias. Por exemplo, o comm (CVE-2026-35346) usava from_utf8_lossy em bytes brutos de arquivos de entrada, corrompendo silenciosamente a saída ao substituir caracteres inválidos por U+FFFD. A correção é permanecer com bytes, usando Write::write_all em vez de print!. A regra é escolher o tipo apropriado para a situação: em código de sistema estilo Unix, use Path e PathBuf para caminhos, OsString para variáveis de ambiente e Vec<u8> ou &[u8] para conteúdo de fluxo. Tentar convertê-los para String para facilitar a formatação pode levar à corrupção. UTF-8 é uma excelente escolha padrão para strings de aplicativos, mas não deve ser a padrão para operações com bytes brutos em ferramentas Unix. Cada panic!, unwrap, expect, índice de slice, operação aritmética não verificada ou from_utf8 em dados de entrada controlados por um atacante é um potencial ataque de negação de serviço (DoS), pois panic! encerra o processo. Se o seu programa roda em cron jobs, pipelines de CI ou scripts de shell, isso pode levar à paralisação completa do sistema. O exemplo canônico é o sort --files0-from (CVE-2026-35348), que chamava expect() para cada conversão de nome de arquivo para UTF-8, falhando em todo o processo se um único caminho não fosse UTF-8. A regra é transformar entradas ruins em erros, não em pânicos. Em código que lida com dados não confiáveis, use ?, get, checked_*, try_from para sinalizar erros reais. A propagação de erros, em vez de descartá-los, é crucial. Bugs como chmod -R e chown -R retornando o código de saída do último arquivo processado, em vez do pior, podem mascarar falhas. O dd chamando Result::ok() em set_len() para simular o comportamento do GNU /dev/null resultou na criação de arquivos de destino parcialmente gravados quando o disco estava cheio. A regra é não descartar informações de erro significativas. Para evitar isso, um padrão simples é registrar o pior código de saída e sair com ele. Se você usa .ok() para descartar um Result, deixe um comentário explicando por que essa falha específica pode ser ignorada com segurança.

É surpreendente que um número considerável de CVEs não se refira a código inseguro, mas sim a um comportamento diferente do GNU, no qual scripts de shell se baseiam. O exemplo mais claro é kill -1 (CVE-2026-35369). O GNU interpreta -1 como "sinal 1" e solicita um PID. A uutils interpreta como "enviar sinal padrão para PID -1", o que no Linux significa todos os processos visíveis. Um erro de digitação se transforma em um desligamento de sistema. A regra é: compatibilidade de bugs é um recurso de segurança. Ao escrever uma nova implementação de uma ferramenta estabelecida, a compatibilidade de bugs em códigos de saída, mensagens de erro, casos de borda e semântica de opções é um recurso de segurança. Se o comportamento do seu programa difere do original, algum script de shell inevitavelmente tomará a decisão errada. Para a uutils, agora é executado um conjunto de testes GNU coreutils no CI, o que é a escala correta de proteção contra essa classe de bugs. O CVE-2026-35368, o pior bug da auditoria, é a execução remota de código root em chroot. O bug é notável se você souber onde procurar (um chroot seguido por uma chamada de função que carrega uma biblioteca dinâmica), mas é completamente não óbvio na primeira leitura do código. O padrão simplificado da utilidade chroot envolve chamar get_user_by_name após o chroot, o que carrega bibliotecas compartilhadas da nova raiz do sistema de arquivos, permitindo que um atacante execute código como uid 0. O GNU chroot resolve o usuário antes de chamar chroot. A regra é: resolva as entradas antes de cruzar limites de confiança. Após cruzar um limite, qualquer chamada de biblioteca pode executar código do atacante. A compilação estática não ajuda, pois get_user_by_name usa NSS, que executa dlopen de módulos libnss_* no ambiente de execução.

Talvez você esteja pensando: "Nossa, isso é um monte de bugs! Talvez Rust não seja tão seguro quanto eu pensava?". Essa seria uma conclusão equivocada. É importante notar que nada do seguinte aconteceu: estouro de buffer, uso após liberação, liberação dupla, corridas de dados para estado mutável compartilhado, desreferenciamento de ponteiro nulo, leitura de memória não inicializada. Graças a isso, embora as ferramentas estivessem (e provavelmente ainda estejam) com bugs, elas nunca tiveram um bug explorável para ler memória arbitrária. O GNU coreutils apresentava CVEs em todas essas categorias. A diferença é notável quando se compara o mesmo tempo de atividade de desenvolvimento. O tipo de bug mais interessante vive na fronteira entre nosso ambiente Rust controlado e o mundo externo confuso e caótico, onde caminhos, bytes, strings e chamadas de sistema se misturam em um triste emaranhado. E essa se tornou a nova fronteira de segurança para o código de sistema moderno. Se você escreve código de sistema em Rust, considere esta lista de CVEs como um checklist. Procure em sua base de código por from_utf8_lossy, chamadas unwrap() aleatórias, Result ignorados, File::create e comparações de string com /. Além disso, sobre um tópico relacionado, escrevi um post sobre Padrões para Programação Defensiva em Rust. Rust correto é Rust idiomático. Quando penso em "Rust idiomático", não penso primeiro em correção. Afinal, não é tarefa do compilador garantir isso? Penso primeiro em padrões elegantes de iteradores, assinaturas de métodos ergonômicas, imutabilidade ou trabalho inteligente com expressões. Mas nada disso importa se o código estiver incorreto, e o compilador está longe de ser perfeito em garantir a correção. É por isso que temos idiomas não apenas para escrever código mais elegante, mas também para escrever código correto. Eles se tornaram a essência da experiência da comunidade, que aprendeu, muitas vezes dolorosamente, qual código sobrevive ao contato com a realidade e qual não. A realidade raramente é tão ideal quanto as abstrações que associamos a ela. Um sinal de sistemas confiáveis em qualquer idioma é a disposição de refletir essa imperfeição em vez de tentar escondê-la. Rust nos fornece ferramentas incríveis para isso, e o compilador faz uma grande parte do trabalho por nós. Mas a parte que ele não pode alcançar, a fronteira entre nosso programa e todo o resto, ainda precisa ser implementada corretamente por nós. O sistema de tipos pode codificar muito, mas não as condições que não estão sob seu controle, como o tempo decorrido entre duas chamadas de sistema. Assim, Rust idiomático não é apenas código que o verificador de empréstimos aprova ou que o clippy aceita. É código cujos tipos, nomes e fluxo de controle comunicam a verdade sobre o sistema em que está sendo executado. E essa verdade às vezes é feia. Pode implicar o uso de descritores de arquivo em vez de caminhos, OsStr em vez de String, ? em vez de unwrap, e a necessidade de compatibilidade de bugs em vez de semântica pura. Tudo isso não é tão bonito quanto a versão que você desenhou no quadro branco, mas é mais honesto. É preciso ser honesto com o GNU: o GNU coreutils tem quarenta anos, e os desenvolvedores tiveram muito tempo para descobrir e corrigir essa classe de bugs. E não sabemos se não há bugs de segurança de memória no código reescrito em Rust, apenas que nenhum foi encontrado na auditoria. No entanto, a diferença é perceptível ao comparar o mesmo tempo de atividade de desenvolvimento. Vale a pena notar que a classe de bugs TOCTOU Path/PathBuf é, de certa forma, mais fácil de evitar em C do que em Rust. O código C usa naturalmente um descritor de arquivo aberto e a família de chamadas de sistema *at (openat, fstatat, unlinkat, mkdirat), e a maioria das chamadas de sistema de criação aceita um argumento mode diretamente. As APIs de alto nível std::fs do Rust abstraem o descritor de arquivo e operam em valores &Path, tornando a chamada com resolução de caminho repetida o caminho de menor resistência. As APIs que usam descritores estão disponíveis em todas as plataformas Unix, o Rust simplesmente não as prioriza.

🛡️⚡

Pare de pesquisar. Comece a hackear.

O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.

Testar grátis por 7 dias →

Sem cartão para começar · Planos a partir de R$49/mês

📤 Compartilhar & Baixar

🧰 Ferramentas recomendadas

Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.

Aprendendo Kali Linux: Teste de segurança, pentest e hacking ético

Aprendendo Kali Linux: Teste de segurança, pentest e hacking ético

Com centenas de ferramentas pré-instaladas, a distribuição Kali Linux facilita o trabalho de os profissionais de segurança começarem a fazer testes de segurança rapidamente. No entanto, com mais de 600 ferramentas em seu arsenal, o Kali Linux também pode ser desafiador. A nova edição deste prático livro abrange as atualizações nas ferramentas e inclui uma melhor abordagem da análise forense e da engenharia reversa. Ric Messier, autor, não fica apenas no teste de segurança, mas também faz uma abordagem sobre a execução de análise forense, incluindo a análise em disco e na memória, assim como alguma análise básica de malware. • Explore as diversas ferramentas disponíveis no Kali Linux • Entenda o valor do teste de segurança e examine os tipos de teste disponíveis • Aprenda os aspectos básicos do pentest em todo o ciclo de vida do ataque • Instale o Kali Linux em vários sistemas, tanto físicos quanto virtuais • Descubra como usar diferentes ferramentas destinadas à segurança • Estruture um teste de segurança baseado nas ferramentas do Kali Linux • Estenda as ferramentas do Kali para criar técnicas de ataque avançadas • Use o Kali Linux para ajudar a criar relatórios quando o teste terminar “A abordagem concisa, clara e baseada na experiência adotada por Ric Messier para a introdução do Kali Linux e dos testes de cibersegurança é incomparável. Este livro é uma leitura excelente e acessível para iniciantes e um recurso valioso para qualquer pessoa.” —Alexander Arlt, Consultor sênior de segurança, Google

Ver na Amazon
Gshield 2 em 1 Hub Extensor Conector USB-C + USB-A e Adaptador de Rede Ethernet LAN RJ45 com 3 Entradas USB 3.0 até 5 Gbps em Liga de Alumínio para Computador e Notebook, Cinza

Gshield 2 em 1 Hub Extensor Conector USB-C + USB-A e Adaptador de Rede Ethernet LAN RJ45 com 3 Entradas USB 3.0 até 5 Gbps em Liga de Alumínio para Computador e Notebook, Cinza

Compatível com portas USB-C e USB-A, ideal para ampliar a conectividade de dispositivos como MacBook Pro e outros com portas USB-C. Inclui um adaptador USB-A extra, proporcionando uma conexão Ethernet estável e veloz de até 1 Gbps, perfeita para filmes, jogos online e videoconferências. Oferece três portas USB 3.0 com velocidades de transferência de até 5 Gbps, permitindo conectar mouse, teclado, discos rígidos e outros periféricos. Fabricado em alumínio durável, garantindo longa vida útil e resistência ao uso diário. Design compacto e leve, ideal para viagens de negócios e uso diário, facilitando o transporte e armazenamento. Funciona com Windows 10/8.1/8, Mac OS e Chrome OS, oferecendo versatilidade incomparável para diversas necessidades de conectividade. Assegura uma conectividade estável e rápida, perfeita para tarefas exigentes como transferência de dados, streaming e mais.

Ver na Amazon
Hacking APIs: Breaking Web Application Programming Interfaces

Hacking APIs: Breaking Web Application Programming Interfaces

Hacking APIs is a crash course on web API security testing that will prepare you to penetration-test APIs, reap high rewards on bug bounty programs, and make your own APIs more secure. You'll learn how REST and GraphQL APIs work in the wild and set up a streamlined API testing lab with Burp Suite and Postman. Then you'll master tools useful for reconnaissance, endpoint analysis, and fuzzing, such as Kiterunner and OWASP Amass. Next, you'll learn to perform common attacks, like those targeting an API's authentication mechanisms and the injection vulnerabilities commonly found in web applications. You'll also learn techniques for bypassing protections against these attacks. In the book's nine guided labs, which target intentionally vulnerable APIs, you'll practice: Enumerating APIs users and endpoints using fuzzing techniques Using Postman to discover an excessive data exposure vulnerability Performing a JSON Web Token attack against an API authentication process Combining multiple API attack techniques to perform a NoSQL injection Attacking a GraphQL API to uncover a broken object level authorization vulnerability

Ver oferta
Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition

Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition

Up-to-date strategies for thwarting the latest, most insidious network attacks This fully updated, industry-standard security resource shows, step by step, how to fortify computer networks by learning and applying effective ethical hacking techniques. Based on curricula developed by the authors at major security conferences and colleges, the book features actionable planning and analysis methods as well as practical steps for identifying and combating both targeted and opportunistic attacks. Gray Hat Hacking: The Ethical Hacker's Handbook, Sixth Edition clearly explains the enemy's devious weapons, skills, and tactics and offers field-tested remedies, case studies, and testing labs. You will get complete coverage of Internet of Things, mobile, and Cloud security along with penetration testing, malware analysis, and reverse engineering techniques. State-of-the-art malware, ransomware, and system exploits are thoroughly explained. Fully revised content includes 7 new chapters covering the latest threats Includes proof-of-concept code stored on the GitHub repository Authors train attendees at major security conferences, including RSA, Black Hat, Defcon, and B-Sides

Ver na Amazon
Bloqueador USB de privacidade de porta USB para PC, notebook, bloco de laptop,

Bloqueador USB de privacidade de porta USB para PC, notebook, bloco de laptop,

Proteção de privacidade aprimorada: protege o link de transmissão de dados para evitar roubo de informações, fornecendo proteção de segurança robusta que protege a privacidade do usuário durante transferências de arquivos e garante uma conexão segura para interações de dispositivos sem preocupações em vários ambientes Uso a longo prazo: a camada protetora resistente ao desgaste, combinada com um corpo de metal resistente, oferece gerenciamento de calor confiável e qualidade duradoura durante o uso diário Entrega eficiente de energia: a tecnologia de chip inteligente garante a identificação automática dos requisitos de energia, fornecendo carregamento eficiente alinhando-se com vários protocolos de carregamento rápido para maior conveniência Proteção contra sobrecarga: evitando riscos de sobrecarga, este bloqueador de dados USB protege a vida útil da bateria e garante um desempenho estável, mantendo um fluxo estável de energia para melhorar a longevidade do dispositivo de forma eficaz Prático de transportar: com atenção à portabilidade, este bloqueador de dados USB oferece um design compacto que é leve e fácil de transportar, melhorando a conveniência do usuário e operação eficiente

Ver na Amazon

📩 Newsletter MundiX

Receba novidades de cibersegurança + um checklist de pentest grátis. Sem spam.

Ao assinar você concorda em receber e-mails. Cancele quando quiser.