Abuso da infraestrutura do Go
(reverse.put.as)- O checksum database e o proxy público de módulos do Go podem aceitar até repositórios sem código Go, criando um caminho para carregar dados arbitrários de repositórios Git na infraestrutura do Go e baixá-los depois
- Uma requisição para
sum.golang.org/lookup/$module@$version, ao encontrar uma versão de módulo ainda não registrada, busca no servidor de origem, e nesse processo o zip do repositório também passa a ser servido emproxy.golang.org - O repositório Ruby do Homebrew e um fork de repositório Rust apareciam no checksum database, e um experimento mostrou que não só novos módulos Go, mas também repositórios sem arquivos Go podem ser registrados como pseudo-version
- O zip do módulo tem limite de 500 MiB tanto comprimido quanto descomprimido, mas isso ainda é grande o suficiente para contornar restrições de download em máquinas de desenvolvedores e pipelines CI/CD, armazenar payloads e implementar C2
- Entre cerca de 1,59 milhão de caminhos únicos em
sum.golang.org, aproximadamente 1,51 milhão são caminhos do GitHub, algo em torno de 95%, expondo ao mesmo tempo a dependência do ecossistema Go em relação ao GitHub e a possibilidade de abuso do proxy público
Repositórios não-Go encontrados no checksum database do Go
- Ao examinar o checksum database do Go,
github.com/homebrew/homebrew-coreapareceu com muita frequência na tabelamodulesgithub.com/homebrew/homebrew-core: 39.438github.com/Homebrew/homebrew-core: 30.896github.com/concourse/concourse: 25.372github.com/openshift/release: 24.065github.com/cilium/cilium: 22.138
- O repositório do Homebrew é conhecido por usar Ruby, e nem no repositório nem nos arquivos clonados foram encontrados
go.modou arquivos-fonte Go - A diferença de maiúsculas e minúsculas é explicada pela regra de case encoding da documentação do Go
- Letras maiúsculas são codificadas como
!seguido da correspondente minúscula, permitindo armazenar juntosexample.com/Meexample.com/mmesmo em sistemas de arquivos que não diferenciam maiúsculas de minúsculas
- Letras maiúsculas são codificadas como
github.com/Edu4rdSHL/rust-headless-chrometambém aparece no checksum database, embora seja um fork de um repositório Rust sem relação com Go
Como o /lookup busca repositórios
- Segundo o Go Modules Reference da documentação de módulos Go, ao consultar o checksum database o comando Go primeiro busca os dados do registro no endpoint
/lookup - Se a versão do módulo ainda não tiver sido registrada no log, o checksum database tenta buscar esse módulo no servidor de origem antes de responder
- O formato do endpoint é
$base/lookup/$module@$version- Ele retorna o número do registro no log para a
$versionde$module, a linha dego.sume a descrição da árvore assinada
- Ele retorna o número do registro no log para a
- Ao consultar uma pseudo-version de
github.com/homebrew/homebrew-core, são retornados o registro de checksum e o hash dego.mod - Se o repositório não tiver tag de versão, entra em cena a regra de pseudo-version do Go
Experimento de registro de um novo módulo Go
- Após criar o novo módulo Go
github.com/gdbinit/fluxmatter, foi feita uma requisiçãolookuppara verificar se ele havia sido registrado - A consulta com
@latestretornou erro dizendo que não era uma versão canônicabad request: version "latest" is not canonical
- A consulta com
@v0.0.0retornou erro dizendo que aquela revisão era desconhecidanot found: ... invalid version: unknown revision v0.0.0
- Mas, ao sincronizar novamente o checksum database e consultar outra vez, o módulo já havia sido registrado
github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z
proxy.golang.org/github.com/gdbinit/fluxmatter/@latestretornou a pseudo-version e a informação de origem no GitHub, e o zip dessa versão pôde ser baixado e ter a compressão validada- Na semeadura inicial, nem foi preciso informar a versão exata: bastou uma consulta lookup contendo o caminho do módulo e um valor que parecesse uma versão
Até repositórios sem código Go são carregados no proxy público
- O mesmo experimento também funcionou com
github.com/gdbinit/readmem, um repositório sem nenhum código Go - A requisição
lookupretornou erro dizendo não conhecer a revisãov0.0.0, mas o checksum database mesmo assim o registrou com pseudo-versiongithub.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z
- O
@latestdeproxy.golang.orgretornou a pseudo-version desse repositório e a informação de origem Git - O zip baixado continha não arquivos Go, mas
Entitlements.plist,README, arquivos de projeto do Xcode,main.cetc. - O experimento usou um repositório no GitHub, mas isso pode ser possível também em outros sites de hospedagem, desde que o VCS funcione
Dependência do GitHub e limites de tamanho
- O checksum database tinha 1.591.375 caminhos únicos, dos quais 1.515.957 eram caminhos
github.com% - Cerca de 95% dos caminhos únicos estão hospedados no GitHub {p:95}
- Esse número é uma estatística bruta, sem remover forks ou alvos que não sejam código Go de fato
- Os zips de módulos Go seguem as restrições de caminho e tamanho de arquivos
- arquivo zip do módulo: máximo de 500 MiB
- tamanho total descomprimido dos arquivos: também máximo de 500 MiB
- arquivo
go.mod: máximo de 16 MiB - arquivo
LICENSE: também máximo de 16 MiB
- Esses limites existem para mitigar ataques de negação de serviço contra usuários, proxies e outras partes do ecossistema de módulos
- Ainda assim, 500 MiB é um tamanho grande o bastante para cenários de abuso
Cenários possíveis de abuso
- O proxy público do Go pode ser usado para contornar restrições de destino para download em máquinas de desenvolvedores ou servidores de CI/CD
- assumindo um ambiente sem
GOPROXYprivado - um malware pode enviar o payload para um repositório e depois baixá-lo do proxy quando necessário
- mesmo que a fonte original suma, a entrada no checksum database pode deixar apenas um pequeno vestígio
- assumindo um ambiente sem
- Um DoS contra
proxy.golang.orgpode não ser trivial de executar- é possível solicitar que o proxy baixe repositórios Git arbitrários
- um ataque possível seria coletar muitas URLs do GitHub e enviar muitas requisições à API
lookup - a implementação do servidor é desconhecida, mas pode haver limites de paralelismo via fila de trabalho ou algo semelhante
- também é possível que as proteções de banda do GitHub entrem em ação
- existe ainda a hipótese de DoS voltado a armazenamento, mas isso permanece especulativo
- É fácil construir um C2 (command and control) sobre
proxy.golang.org- a consulta
@latestpermite descobrir a versão mais recente de um módulo específico - o payload pode ser um arquivo simples ou pode ser ocultado dentro de
go.modou de arquivos-fonte Go - para evitar depender de um único repositório, pode-se usar um module DGA
- a consulta
Fluxo de download do C2
- Para receber comandos, um implant só precisaria seguir estes passos
- fazer uma requisição a
https://proxy.golang.org/module_path/@latest - extrair do JSON retornado a pseudo-version ou a version
- baixar o zip com
https://proxy.golang.org/module_path/@v/version.zip - descompactar o conteúdo do zip e interpretar os comandos
- fazer uma requisição a
- Esse fluxo é simples a ponto de poder ser implementado em menos de 300 linhas de código Go
Conclusão e perguntas em aberto
- O checksum database e o proxy do Go podem, conforme o procedimento documentado, buscar módulos ainda não registrados no servidor de origem, armazená-los e servi-los depois
- No estado atual isso não parece um problema crítico da infraestrutura do Go, mas é algo fácil de abusar e com espaço para melhorias
- Pode haver uma razão documentada ou não pública para permitir que repositórios que não são Go sejam carregados no proxy e no checksum database
- Para verificar se alguém já está abusando disso, seria necessário investigar cerca de 1,6 milhão de repositórios únicos e aproximadamente 22 milhões de entradas em um database local atualizado
- Continua em aberto por que alguns projetos legítimos non-Go estão no database
1 comentários
Opiniões do Hacker News
Qualquer serviço online em que usuários enviam material e esse material aparece publicamente acaba sendo usado para comando e controle, violação de direitos autorais e hospedagem de CSAM.
Isso vale especialmente para serviços difíceis de bloquear por terem usos importantes além de hospedagem de arquivos; já aconteceu com Twitter[1], Telegram[2] e infraestrutura de chaves PGP[3], sem falar em alvos óbvios como o GitHub.
[1] https://pentestlab.blog/2017/09/26/command-and-control-twitt...
[2] https://www.blazeinfosec.com/post/leveraging-telegram-as-a-c...
[3] https://torrentfreak.com/openpgp-keyservers-now-store-irremo...
No caso do Gmail, distribuíam credenciais para permitir login e, então, liam via IMAP os anexos que haviam sido enviados.
Fui SAD-SRE (Spam, Abuse, Delivery) do Google.
Também dá para codificar um arquivo em Base64 dentro de uma string de código Python.
Sou Googler, esta é minha opinião pessoal e não conheço bem essa área.
Espero que a equipe de Go tenha colaborado com o pessoal de GCP e Drive, porque hospedagem de arquivos maliciosos é um problema com o qual o Google lida o tempo todo.
Não é muito diferente de outros endpoints em que o Google já permite que pessoas insiram dados arbitrários.
O Google é muito bom em fazer uma equipe central gerenciar infraestrutura e compartilhá-la com toda a empresa. Exceto quando se trata de apps de mensagens; e, puramente por especulação, imagino que a equipe de Go esteja usando um armazenamento interno de blobs e que também exista uma equipe interna de infraestrutura que automatize resposta a abusos e varredura de arquivos.
Há uma boa quantidade de projetos não Python no PyPI.
Como usuários de Python podem não conseguir compilar código de bibliotecas, é preciso ter a capacidade de distribuir wheels, que são binários compilados.
Esse tipo de código costuma ser escrito em C, mas também pode ser em Golang[1]; não encontrei um exemplo, mas acho que já vi isso ser usado para distribuir aplicações, não só bibliotecas.
Escrever um app em C, colocá-lo no PyPI e dizer aos usuários para instalá-lo com
pip installé bem legal.[1] https://github.com/popatam/gopy_build_wheel_example
Seria como se, no Linux, o
lstivesse sido escrito em Python; acho melhor não entrar nesse jogo.pippassar a exigir que, para baixar pacotes, você estivesse dentro de um ambiente como venv/virtualenv/pipenv/pyenv.pip install cmake, e binários proprietários podem ser instalados comopip install nvidia-cudnn-cu12.Foi um dos motivos que me permitiram abandonar completamente o Homebrew.
Talvez seja uma ideia ingênua, mas não entendo como isso é diferente de enviar arquivos para um repositório do GitHub.
A única diferença é que, no GitHub, é preciso criar uma conta? Também dá para armazenar dados arbitrários no GitHub, e não há limite de 500 MB.
O sistema de módulos do CUE finalmente está sendo lançado, e o MVS é parecido com o do Go, mas foi construído sobre infraestrutura OCI.
Se você se interessa por sistemas de gerenciamento de dependências, estes links podem ser úteis.
proposal: https://github.com/cue-lang/proposal/tree/main/designs/modul...
custom registry: https://cuelang.org/docs/tutorial/working-with-a-custom-modu...
road map: https://github.com/orgs/cue-lang/projects/10/views/8
Os módulos vêm ativados por padrão desde a 0.9.0-alpha-5: https://github.com/cue-lang/cue/releases/tag/v0.9.0-alpha.5
No Go Sum, o projeto Trillian dá suporte ao log de transparência: https://github.com/google/trillian
O plano do CUE é se apoiar em opções do OCI, como attestation.
Brinquei com a ideia de pegar carona, ou melhor, abusar do proxy do golang e do sumdb para criar um log de transparência gratuito para checksums de URLs arbitrárias
https://getsum.pub/
Se você só quer um log de transparência público, a instância pública do rekor do projeto sigstore é bem mais adequada
https://www.sigstore.dev/
https://docs.sigstore.dev/logging/overview/
Talvez eu seja burro, mas não entendo exatamente qual é o problema aqui
O proxy armazenar em cache repositórios que não são Go pode ser um pouco desperdício, mas mesmo sem isso, se você fizer ele armazenar em cache repositórios Go, não daria para fazê-lo armazenar dados arbitrários de qualquer forma?
Se eu não estiver deixando passar algo, soa como algo totalmente sem importância
A novidade aqui parece ser só que um proxy público sem segurança aceita o que deve proxiar e o disponibiliza publicamente de uma forma sem segurança
O texto diz que algumas redes monitoradas podem confiar mais em URLs de proxy do golang do que em URLs web arbitrárias, podendo ser usadas para contornar filtros de reputação etc., mas já existem várias maneiras de fazer isso, e esta não parece especial
Fugindo do assunto, mas vi o domínio e o trocadilho me pareceu suspeito, então conferi put.as, e era mais ou menos o que eu esperava
É uma issue já conhecida: https://github.com/golang/go/issues/31866
.mode.gona raiz?Ele e o Aaron criaram o Athens e, até onde sei, o Marwan escreveu a primeira implementação do protocolo de download do Go na qual o Athens foi baseado
O interessante nessa issue é que o Athens já usa o comando
go mod download -jsonmencionado como verificação prévia de validação de módulosEm geral, se o repositório passa como um módulo que o comando de módulos do Go entende, o Athens o serve
Dito de forma mais restrita, você precisa conseguir criar versões de módulo, pseudoversões e
+incompatible, e esse módulo e suas dependências precisam gerar checksums válidosO checksum de módulo atualmente tem relação com incluir o
.mod, todos os arquivos e cada dependência recursivamenteEntão, como o autor disse, só com um programa Go básico você consegue, por design, ter bastante espaço para arquivos arbitrários
O W3C preparou a base para que tudo na web pudesse ser fortemente cacheado, então é estranho haver tão poucos caches de proxy de uso geral
Será que os publicadores estão enviando respostas com
Cache-Control: max-agecurto ouVary: Cookiesem necessidade?Será que os ISPs estão pagando caro demais por trânsito em vez de peering?
Por exemplo, em sites que não usam HTTPS, um proxy do ISP pode inserir anúncios
Downloads de software normalmente têm assinaturas e checksums, mas conteúdo arbitrário não costuma ter muito disso