07 - CI-CD e Supply Chain de Software
video: https://youtu.be/... no frontmatter da nota.CI-CD e Supply Chain de Software
Nota 07 do capítulo 26 (CTI e Threat Hunting em Ambientes Modernos), cobrindo o supply chain de software e o envenenamento de pipelines de CI-CD como superfície de ataque.
A nota disseca os incidentes paradigmáticos SolarWinds, XZ Utils e 3CX, mapeia as TTPs de pipeline poisoning e entrega telemetria e consultas para caçar abuso de runner e exfiltração de segredos.
Dialoga com a nota 05 deste capítulo em telemetria de cloud, com o DaC do capítulo 18, cuja própria esteira de detecções é um alvo, e com os controles PRT.4 e SIM do capítulo 15.
1. Contexto e Posicionamento na Biblioteca
A esteira de CI-CD é o único lugar da infraestrutura onde a organização executa, com credenciais de produção e segredos de cloud, código que acabou de chegar de fora: dependências de registries públicos, patches de terceiros, contribuições anônimas e ações de marketplace. O adversário percebeu que comprometer um elo dessa cadeia entrega escala industrial, porque um único merge malicioso se propaga por push automático para milhares de consumidores, assinado digitalmente e sem qualquer comportamento anômalo no endpoint. Esta nota estrutura a defesa dessa superfície em quatro frentes: anatomia da cadeia, lições dos incidentes que definiram o tema, TTPs de pipeline poisoning e caça sobre a telemetria da esteira.
O posicionamento na biblioteca é duplo. Como superfície de ataque, a nota pertence ao bloco de ambientes modernos do capítulo 26, ao lado da telemetria de cloud da nota 05 e do runtime de containers da nota 06. Como vítima em potencial, a própria biblioteca opera detecção como código pelo capítulo 18, e o repositório de regras Sigma e os pipelines de deploy do capítulo 18 herdam diretamente os controles e caças que aqui são apresentados, incluindo a nota 04 do bloco de DaC sobre testes automatizados e CI-CD para detecções.
2. Anatomia da Supply Chain de Software
A cadeia que entrega software à produção se compõe de quatro estoques de confiança, e cada um deles é uma fronteira onde o controle de segurança costuma ser mais fraco que o valor protegido. A lista abaixo descreve cada elo e o porquê do seu apelo ao adversário.
- Dependências: bibliotecas open source dos registries npm, PyPI e Maven entram no build por resolução automática, e ataques de typosquatting, dependency confusion e version tag maliciosa exploram justamente a automação que ninguém revisa. O caso do pacote event-stream e os surtos recorrentes de pacotes maliciosos em npm e PyPI mostram que o elo mais fraco é a confiança transativa.
- Pessoas e contas de mantenedor: a reputação de quem mantém o projeto é parte da cadeia de confiança, e o sequestro de conta de mantenedor vale tanto quanto o comprometimento de servidor. Token de mantenedor exposto em repositório público ou sessão sequestrada transforma a publicação de release em vetor de distribuição.
- Runners: as máquinas que executam os jobs de build têm acesso a segredos, a redes internas e, no caso de runners self-hosted, ao cache de execuções anteriores. Workflows disparados por pull request em runners self-hosted combinam código não confiável com credencial confiável na mesma shell, que é a definição de incidente latente.
- Segredos: tokens de deploy, credenciais de cloud e chaves de assinatura ficam injetados como variáveis de ambiente no runtime do job, visíveis para qualquer etapa que resolva imprimi-las ou enviá-las para fora. Um único step com echo mal colocado, ou com curl apontando para domínio do atacante, esvazia o cofre inteiro.
- Artefatos: o binário ou a imagem que sai do build é a materialização final da cadeia, protegida por assinatura digital que confere autenticidade de origem, e não integridade de conteúdo. Quando o próprio build está comprometido, a assinatura legítima lava o artefato malicioso, como SolarWinds e 3CX demonstraram em escala.
O princípio de defesa que organiza esses quatro elos é a segmentação de confiança: código de origem externa nunca roda com segredo de produção acessível, runner de PR é efêmero e sem rede interna, segredo de job tem escopo mínimo e curto prazo, e artefato carrega proveniência verificável, como attestations no padrão SLSA e assinatura com sigstore. A ausência de qualquer um desses quatro pilares define exatamente onde a caça da seção 6 precisa começar.
3. Incidentes Paradigmáticos e Lições
SolarWinds (2020) permanece o caso de referência de comprometimento de build: o adversário habitou o ambiente de compilação da empresa, injetou o backdoor SUNBURST no processo de build do produto Orion e fez a própria vítima assinar e distribuir o malware como atualização oficial para cerca de dezoito mil organizações. A lição estrutural é que o controle de fronteira não enxerga esse ataque, porque o compromisso está dentro do processo legítimo, e a defesa real é telemetria do build, controle rigoroso do ambiente de compilação e validação comportamental do artefato entregue.
XZ Utils (2024) é a lição oposta, de comprometimento social e de empacotamento: o operador manteve o projeto por anos construindo reputação, implantou o backdoor CVE-2024-3094 em artefatos de distribuição via camadas de teste ofuscadas que só se ativavam no build de pacote, e o alvo era o sshd de sistemas que ligavam liblzma ao systemd. A detecção não veio de ferramenta de segurança, e sim de engenharia observante que notou latência anômala no sshd, o que valida o princípio do capítulo 15 de que baseline de comportamento, inclusive de performance, é superfície de detecção.
3CX (2023) demonstra o efeito cascata: atacantes afiliados a operação norte-coreana comprometeram a infraestrutura da própria empresa e injetaram carga no instalador assinado do desktop app, atingindo organizações que já haviam sido vítimas do software de trading comprometido no elo anterior. A cadeia de confiança quebrada em dois níveis mostra que o hunting de supply chain precisa olhar para o supplier do supplier, e que a resposta a incidente de terceiro deve reavaliar todos os artefatos assinados pelo fornecedor comprometido, e não apenas o produto afetado.
Casos complementares fecham o quadro: o Codecov (2021) vazou credenciais de CI através de script de upload comprometido, e a proliferação de ações de marketplace comprometidas, como o caso amplamente explorado em 2025 com a ação tj-actions/changed-files entregando segredos de runner para logs públicos, mostra que o vetor preferido do momento é a dependência de pipeline, e não mais a dependência de biblioteca.
A comparação estruturada desses casos evidencia que cada incidente quebrou um elo diferente, o que reforça a necessidade de telemetria em toda a cadeia em vez de controle único. A tabela abaixo resume o elo comprometido e o controle que mais teria reduzido o impacto em cada caso.
| Caso | Elo comprometido | Vetor de entrada | Controle decisivo |
|---|---|---|---|
| SolarWinds | Ambiente de build do fornecedor | intrusão prolongada no build system | telemetria de build e proveniência do artefato |
| XZ Utils | Empacotamento upstream | engenharia social do mantenedor | baseline comportamental do serviço em produção |
| 3CX | Instalador assinado de terceiro | comprometimento interno do fornecedor | reavaliação de artefatos assinados pós-incidente |
| Codecov | Script de upload do CI | modificação do uploader | rotação de segredos expostos a runners |
| tj-actions (2025) | Ação de pipeline de terceiros | tag movida para commit malicioso | pin de ação por hash de commit |
4. TTPs de Pipeline Poisoning
O envenenamento de pipeline tem um repertório técnico próprio, com ATT&CK mapeável e assinaturas distintas da exploração de endpoint. A tabela abaixo consolida as seis famílias operacionais que o hunter deve dominar.
| TTP | ATT&CK | Assinatura típica |
|---|---|---|
| Dependência comprometida | T1195.001 | egress do runner para domínio novo durante install, release fora do padrão do maintainer |
| Comprometimento de build e artefato | T1195.002 | build executado por identidade ou runner fora do padrão, alteração de script de build |
| Abuso de runner self-hosted | T1078, T1059 | job de fork PR rodando em runner com acesso à rede interna |
| Exfiltração de segredo | T1552, T1041, T1567.002 | segredos em log, POST para webhook ou domínio não aprovado na janela do job |
| Tag tampering e release manipulada | T1565.001 | movimentação ou recriação de tag, force push em branch de release |
| Persistência em workflow | T1053.003, T1078 | workflow com schedule criado fora de PR revisado |
O padrão transversal dessas famílias é o abuso do mecanismo legítimo de entrega, e é por isso que a detecção por assinatura de payload falha sistematicamente nesse domínio. O que distingue o uso legítimo do malicioso não é o comando, e sim o contexto: quem disparou, de qual runner, para qual destino de rede, em qual momento do ciclo de release e com qual aprovação. Toda consulta da seção 6 é construída sobre esse princípio de contexto.
Vale registrar que essas famílias não são mutuamente exclusivas e se combinam em campanhas reais com economia de esforço: o comprometimento de uma ação de marketplace (T1195.001) entrega imediatamente o acesso a runner (T1078), que expõe segredos (T1552), que financiam ou viabilizam o comprometimento de artefato (T1195.002). O hunting que trata cada família isoladamente enxerga o sintoma; o que encadeia eventos por identidade e por janela temporal enxerga a operação.
5. Telemetria e Hunting na Esteira de CI-CD
A telemetria de supply chain se organiza em quatro planos que precisam convergir no mesmo SIEM, porque a correlação entre eles é o que distingue incidente de ruído. O plano de auditoria do forge entrega o GitHub Audit Log (com streaming nativo para SIEM nas edições empresariais) e o GitLab com audit events, cobrindo ações como git.clone e git.push, mudanças de permissão em repositório, criação e alteração de workflows, solicitação de personal access token e resolução de alertas de secret scanning. O plano de execução entrega os logs de runner, com nome do job, workflow, step, duração e identidade disparadora. O plano de rede entrega o egress do pool de runners via proxy, que é a única fonte confiável de destino de conexão. O plano de integridade entrega o registro de artefatos com hash, assinatura e attestations.
| Plano | Fonte principal | Pergunta de caça |
|---|---|---|
| Auditoria do forge | GitHub Audit Log, GitLab audit events | Quem alterou workflow, permissão ou tag, e fora de qual esteira |
| Execução | Logs de runner | Qual step executou, com qual segredo acessível e disparada por quem |
| Rede | Proxy do pool de runners | Para onde o job se conectou e se o destino está no baseline |
| Integridade | Registry, sigstore, attestations SLSA | O artefato em produção corresponde ao build aprovado |
A heurística de alto valor nasce do cruzamento de planos, e não de qualquer plano isolado. Job cujo autor é conta externa, executado em runner com acesso interno, conectando-se a domínio fora do baseline na janela do build é uma sentença, ainda que nenhum dos quatro sinais isolados passe o limiar de alerta. A nota 04 do capítulo 02 fornece a disciplina de avaliação de fonte, e a consulta da seção 6 implementa o cruzamento.
6. Exemplo de Caça: Exfiltração de Segredos em Pipeline
A caça de exfiltração de segredo começa pelo plano de rede, porque o segredo só interessa ao adversário quando sai da esteira. A consulta abaixo, em SPL, cruza o egress do pool de runners com o inventário de jobs e destaca destinos fora do baseline na janela de execução.
index=proxy src_subnet="10.42.0.0/16"
| lookup runners_cidr.csv src AS src OUTPUT job_id, workflow, repositorio
| lookup dominios_aprovados.csv dest_domain OUTPUT aprovado
| where isnull(aprovado) AND job_id != ""
| stats sum(bytes_out) AS bytes_enviados, dc(dest_domain) AS dominios,
values(dest_domain) AS destinos
BY job_id, workflow, repositorio, src
| where bytes_enviados > 10000 OR dominios >= 1
| sort - bytes_enviados
A segunda etapa desce ao plano de execução e ao próprio repositório, com duas verificações complementares. A varredura de workflows que combinam o gatilho de pull request com execução em runner privilegiado localiza o padrão de abuso de runner, e a inspeção dos steps que referenciam segredos junto de comandos de rede localiza o padrão de escoamento direto, como no exemplo abaixo, que é a assinatura textual do incidente antes dele acontecer.
# Workflows com gatilho de PR que rodam código do fork
grep -rlE 'pull_request_target|github\.event\.pull_request\.head' \
.github/workflows/ | sort
# Steps que combinam segredo com chamada de rede no mesmo bloco
grep -rnE '(curl|wget|Invoke-WebRequest).*(secrets\.|TOKEN|KEY|PASSWORD)' \
.github/workflows/ | grep -v 'actions/' | sort
A varredura textual do repositório encontra o padrão, e a detecção permanente do mesmo padrão em runtime fica com uma regra Sigma que observa a criação de processos nos runners. A regra abaixo cruza o binário de rede com a presença de nomes de segredo na linha de comando, e é um exemplo direto de caça promovida a detecção.
title: Post de segredos de CI para destino externo
id: 7f3c1a2b-9e21-4d10-9c6e-5a1b2c3d4e5f
status: experimental
description: Step de pipeline referencia variavel de segredo em comando de rede
logsource:
category: process_creation
product: linux
detection:
selection_cmd:
Image|endswith: /curl
selection_secret:
CommandLine|contains:
- AWS_SECRET_ACCESS_KEY
- GITHUB_TOKEN
- SLACK_WEBHOOK
condition: selection_cmd and selection_secret
tags:
- attack.t1552
- attack.t1041
O terceiro plano da mesma caça é o histórico de auditoria do forge, buscando alterações em workflows e tags fora da janela de release aprovada. Alterações de workflow por identidade fora da lista de mantenedores, criação de tag de release sem PR associado e force push em branch protegida com bypass são os três eventos que, combinados com o egress anômalo da primeira consulta, convertem a suspeita em caso investigativo com linha do tempo completa. A verificação de hipótese segue o padrão do capítulo 07: para cada candidato, confirmar a identidade disparadora no audit log, o segredo acessível no job e a natureza do destino de rede antes de classificar.
A consulta abaixo exemplifica a varredura no SPL sobre o audit log do GitHub, com a ressalva de que o nome exato das ações varia por edição e versão da plataforma, cabendo ao time mapear as strings do seu ambiente.
sourcetype="github:audit" (action="workflow*" OR action="protected_branch*" OR action="repo.access")
| eval hora = strftime(_time, "%H")
| stats values(action) AS acoes, dc(repo.name) AS repositorios,
earliest(_time) AS primeiro, latest(_time) AS ultimo
BY actor, hora
| where repositorios >= 3 OR match(acoes, "protected_branch")
| sort - repositorios
O cruzamento final confronta cada candidata com a lista de mantenedores autorizados, com a janela de release aprovada e com o histórico de atividade da mesma identidade. Falsos positivos previsíveis vêm dos bots legítimos de manutenção, como dependabot e renovate, e por isso a exclusão por identidade de automação documentada é parte da caça, e não um improviso. Encontrada a alteração suspeita, o caminho de resposta segue a ordem inversa da cadeia: revogar o segredo acessado, invalidar o artefato publicado no período, auditar os builds da mesma runner e só então remover a modificação do repositório, porque a ordem contrária alerta o adversário.
7. Erros Comuns e Pontos de Atenção
- Tratar CI como ambiente de confiança: a esteira tem as credenciais mais valiosas da organização e executa código de fora, e a ausência de telemetria de runner no SIEM é o equivalente a não logar o controlador de domínio.
- Confiar na assinatura do artefato como prova de integridade: assinatura valida origem do build, e o incidente SolarWinds é precisamente o caso de assinatura legítima sobre build comprometido; a integridade exige proveniência de build, com attestations SLSA.
- Caçar apenas no código e ignorar o runtime: revisão de PR e scan estático são necessários, mas insuficientes; o egress de runner e o audit log capturam o que passou pela revisão disfarçado de código benigno.
- Compartilhar runners entre confianças: runner self-hosted reaproveitado entre repositórios públicos e internos permite a contaminação por cache e por workspace, e a segmentação por nível de confiança é pré-requisito de qualquer caça sensata.
- Encerrar a resposta no ponto errado: o incidente de supply chain termina em rotação de segredos acessados pelo job comprometido e revogação de artefatos, e não apenas em deletar o workflow malicioso; o raio de explosão é calculado pelo inventário de segredos do runner.
- Subestimar ações de terceiros no marketplace: ações de pipeline são dependências executáveis com update automático, e pinar versão por hash de commit é o controle mínimo que a telemetria assume como baseline.
- Assumir que segredos de organização estão isolados: segredos herdados por todos os repositórios de uma organização transformam um repositório experimental em cofre acessível, e a revisão periódica de escopo é controle ativo, e não documentação.
- Ignorar o egress das ações de terceiros: ações executam no runner com a mesma rede do build, e a ausência de telemetria de proxy cobrindo o pool de runners é ponto cego institucionalizado que nenhuma revisão de código compensa.
O ponto de atenção final é o efeito espelho do problema: a biblioteca inteira opera detecção como código, e o repositório de regras Sigma ou as pipelines de deploy do capítulo 18 são exatamente o ativo que esta nota ensina a proteger. Um hunter que aplica esta caça ao próprio pipeline de detecção está executando contra-inteligência no sentido do capítulo 13, porque o adversário que domina o pipeline de detecção apaga a visão de todo o ambiente.
8. Integração com o Restante da Biblioteca
Dentro do capítulo 26, esta nota fecha o ciclo da aplicação cloud-native: a nota 05 fornece a telemetria de cloud onde os runners rodam e para onde os segredos apontam, a nota 06 cobre o runtime dos containers produzidos pela esteira, e a nota 08 completa o quadro com o SaaS, incluindo o próprio GitHub e o Slack por onde a evidência e o alerta transitam. O capítulo 21 contribui na vertente de monitoramento de superfície de terceiros, essencial para o supplier do supplier.
No plano transversal, o capítulo 18 é a vítima e o beneficiário simultâneo, porque sua esteira de detecção como código deve aplicar os controles aqui descritos, e a ponte hunting-para-deteção do capítulo 18 (ADS framework e hunting-to-detection bridge) é o destino natural das consultas desta nota que se mostrarem estáveis. Os controles SIM do capítulo 15 usam os incidentes da seção 3 como material de emulação em purple team, validando as detecções antes que o incidente real chegue, e o controle INT.3 conecta o tema ao compartilhamento de inteligência, já que indicadores de campanhas de supply chain circulam rápido entre ISACs.