Um invasor usou a conta de um colaborador no GitHub para substituir os arquivos de download de cinco versões já publicadas da ferramenta de linha de comando (CLI) da OVHcloud, na segunda-feira, 19 de outubro. A conta havia sido invadida, e os novos arquivos eram uma versão criada para roubar segredos armazenados no disco de qualquer máquina que a executasse, segundo a provedora de nuvem.
Os arquivos ficaram disponíveis no GitHub por até 94 minutos, entre 12h47 e 14h21 (horário UTC, Tempo Universal Coordenado), e atingiram apenas quem instalou ou atualizou a CLI da OVHcloud nesse intervalo, por meio de um caminho que não verifica o hash (código que confere a integridade do arquivo), nos sistemas macOS, Linux x86_64 (arquitetura de 64 bits) ou arm64 (arquitetura ARM de 64 bits), ou Windows x86_64.
A OVHcloud excluiu os arquivos trocados e publicou, naquela noite, uma versão limpa, a v0.16.0, como release imutável, cujos arquivos o GitHub não permite que ninguém altere. O código-fonte, a tag da versão e o pipeline de compilação da empresa não foram tocados: o build malicioso era um simples go build (comando de compilação da linguagem Go) de um commit posterior, enviado diretamente para as versões publicadas entre abril e setembro.
Quem foi exposto
Na versão v0.15.0, o invasor substituiu seis arquivos: os arquivos para macOS x86_64 e arm64, os arquivos para Linux x86_64 e arm64, o arquivo para Windows x86_64 e o arquivo checksums.txt, de modo que os hashes publicados correspondiam aos novos binários. Os mesmos seis arquivos também foram retirados das versões v0.11.0 a v0.14.0. As compilações para Linux i386, Windows i386, Windows arm64 e WebAssembly (formato binário para navegadores) permaneceram intactas em todas as versões.
A OVHcloud classifica as versões v0.11.0 a v0.15.0 como comprometidas e a v0.10.0 como limpa. Essas cinco versões foram publicadas entre 14 de abril e 16 de setembro. Portanto, uma cópia instalada antes das 12h47 UTC de segunda-feira e não atualizada desde então é a original.
Você recebeu um arquivo trocado se, nesse intervalo e em uma das plataformas citadas, você:
- Executou o script oficial de instalação (curl -fsSL https://raw.githubusercontent.com/ovh/ovhcloud-cli/main/install.sh | sh). Ele baixa o arquivo da versão mais recente e o instala, e nunca verificou um hash.
- Baixou um arquivo compactado na página de releases do GitHub de qualquer uma das cinco versões.
- Executou um job de CI (integração contínua) ou uma etapa de build que busca o arquivo na URL de download da release sem um hash fixado.
- Executou o comando de atualização da própria CLI a partir de uma instalação v0.12.0, v0.13.0 ou v0.14.0, em uma máquina macOS ou Linux onde a ferramenta foi instalada pelo script ou manualmente. O comando baixa o arquivo e substitui o binário sem verificação de hash. Em configurações com Homebrew ou go install, ele apenas exibe o comando a ser executado.
Você não foi afetado se:
- Instalou com Homebrew, cuja versão da cask (receita de instalação do Homebrew) em vigor durante o intervalo fixava o SHA-256 (algoritmo de hash) original de cada arquivo. O Homebrew recusa um download cujo hash não corresponda, portanto o brew install ou o brew upgrade teriam falhado em vez de instalar o arquivo trocado. Alguns relatos iniciais citaram o Homebrew entre os caminhos expostos, mas o The Hacker News verificou o histórico de commits do repositório: nada mudou entre a cask da v0.15.0, de 16 de setembro, e a cask da v0.16.0, na noite de segunda-feira.
- Usou go install ou compilou a partir do código-fonte. A tag v0.15.0 ainda aponta para seu commit de setembro, 760fd72.
- Baixou a imagem Docker. Sua tag latest foi enviada pela última vez em 21 de setembro, quatro semanas antes da troca, e não havia sido recompilada até terça-feira.
- Instalou antes das 12h47 UTC de segunda-feira e não atualizou durante o intervalo.
Como identificar
O cliente que percebeu a troca extraiu as informações de build do novo binário Linux arm64 da v0.15.0 com o comando go version -m, que lê o arquivo sem executá-lo, e publicou o resultado em uma issue no GitHub. Três elementos caracterizam o build malicioso:
- A linha do módulo traz v0.15.1-0.20261013083505-5ead8ef9b43d+dirty, uma pseudo-versão, texto genérico que o Go gera para builds feitos a partir de um commit sem tag, neste caso um commit de 13 de outubro. Uma versão genuína traz seu número de tag: o build limpo da v0.16.0 traz v0.16.0+dirty.
- O commit registrado é 5ead8ef, e não 760fd72, o commit ao qual a tag v0.15.0 aponta.
- Há uma linha -ldflags='-s -w'. O pipeline de release da OVHcloud compila com uma flag de versão que o Go deixa de fora das flags registradas. Por isso, uma versão genuína não mostra nenhuma linha -ldflags.
Um conselho que circula dizendo que qualquer build marcado como +dirty, ou com linhas vcs, é o malicioso acabaria acusando a própria release da OVHcloud. O The Hacker News executou o mesmo comando no binário limpo da v0.16.0 Linux x86_64: ele informa v0.16.0+dirty, vcs.modified=true e o commit a partir do qual foi compilado.
O único hash publicado de um arquivo trocado é o do arquivo Linux arm64 da v0.15.0:
- SHA-256 (arquivo trocado): 3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb
- SHA-256 (arquivo original): 5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0
Um build sem a flag de versão informaria a versão como indefinida, mas executar um binário suspeito para descobrir isso não é recomendado.
O que fazer
O fundador da OVHcloud, Octave Klaba, escreveu no X na segunda-feira à noite que quem foi afetado deve trocar todos os segredos armazenados no disco.
A CLI guarda as credenciais da API (interface de programação de aplicações) da OVHcloud em ~/.ovh.conf e as lê de variáveis de ambiente, segundo sua documentação. O alerta, porém, vale para tudo o mais que estiver no disco, não apenas para esses arquivos.
- Se você executou um build trocado, ou não tem como saber, considere exposto todo segredo legível a partir dessa máquina e faça a rotação de cada um deles. A OpenSourceMalware, que investigou o incidente, recomenda fazer isso a partir de outra máquina.
- Remova o binário e instale a v0.16.0. A versão é imutável e traz uma attestation de release (registro assinado de seus arquivos). A atualização substitui o programa, mas não revoga uma chave que já foi copiada, por isso a rotação deve vir primeiro.
- Verifique um arquivo baixado contra a release com o comando gh release verify-asset v0.16.0 ARQUIVO, conforme documenta o GitHub.
A OVHcloud não informou quais arquivos o binário lê, para onde os envia ou quantas cópias foram baixadas, e nenhuma cópia de um arquivo trocado foi analisada publicamente. A empresa prometeu um relatório detalhado pós-incidente nos próximos dias.
Como aconteceu
Qualquer pessoa com permissão de escrita em um repositório do GitHub pode excluir os arquivos anexados a uma release e enviar novos arquivos com os mesmos nomes. A URL de download não muda, a menos que a imutabilidade de releases esteja ativada.
A configuração, disponível de forma geral desde outubro de 2025, bloqueia os arquivos e a tag de uma release, mas protege apenas as releases criadas depois que ela é ativada.
A OVHcloud não havia ativado a opção. Em uma segunda publicação na noite de segunda-feira, citada pelo Next, Klaba escreveu que o problema decorria de a opção de imutabilidade não estar ativada. Assim, um usuário com as permissões certas podia modificar releases existentes diretamente no GitHub, sem nenhuma verificação do pipeline de CI/CD (integração e entrega contínuas) da empresa. A opção já foi ativada, e os pipelines em si não foram afetados.
As cinco versões antigas agora oferecem apenas os quatro arquivos que o invasor não tocou.
Os envios foram feitos pela conta de um colaborador cujas contribuições aprovadas estão no repositório de documentação da OVHcloud e que não é membro público da organização, segundo a OpenSourceMalware, que montou uma linha do tempo a partir dos registros do lado do servidor do GitHub.
O fluxo de trabalho (workflow) de release nunca foi executado: a lista de execuções não mostra nenhum workflow entre 16 de outubro e o envio da tag v0.16.0, às 16h UTC de segunda-feira. As primeiras verificações não encontraram nenhum outro repositório afetado, escreveu Klaba em sua primeira publicação, e a OVHcloud não informou como as credenciais da conta foram obtidas.
Um job de CI de um cliente percebeu a troca porque fixa o SHA-256 do arquivo Linux arm64 e começou a falhar. Sem um canal privado de reporte no repositório, o cliente abriu uma issue pública, às 13h50 UTC pela linha do tempo da OpenSourceMalware. A OVHcloud excluiu os arquivos trocados cerca de meia hora depois, às 14h21 UTC (16h21 em Paris). A v0.16.0 foi publicada às 17h16 UTC, sob a conta ovh-cds da empresa, com tag assinada.
A questão do GlassWorm
A OpenSourceMalware associa a conta ao GlassWorm, campanha de malware que esconde código em caracteres Unicode invisíveis (padrão internacional de codificação de texto) e busca o próximo estágio no campo memo das transações de uma carteira da rede Solana. Segundo os pesquisadores, quatro repositórios pessoais da conta sofreram force-push (envio forçado) em 5 e 6 de março, com um commit alterado que incluía um carregador desse tipo.
A carteira lida por esse carregador é a mesma citada na campanha ForceMemo do GlassWorm, que enviou malware à força para centenas de repositórios Python em março, usando tokens do GitHub roubados. O The Hacker News confirmou que o arquivo do carregador ainda está em um desses repositórios, com o hash publicado pelos pesquisadores.
Os pesquisadores escreveram que a hipótese de que o mesmo agente fez as duas coisas é 'nossa inferência'. A conta compartilhada é o elo, e nada do binário de outubro foi associado ao GlassWorm, porque nenhuma cópia dele foi preservada.