Em nosso post anterior sobre CI/CD Pipelines, nós vimos como hackear um CI/CD cenário que presumivelmente estava protegido.
Vamos relembrar nosso ponto no post anterior: começamos com alguns pipeline que era vulnerável a Envenenado Indireto Pipeline Execução (I-PPE) e, para resolver, decidimos dividir o pipeline em dois:
- O 1st pipeline (Build CI), seguro para D-PPE e I-PPE, verificaria o código PR, faria a construção e geraria um artefato
- O 2nd pipeline (Test CI), também seguro para D-PPE e I-PPE, verificaria o código base (para evitar a modificação do script de shell) e executaria os scripts originais no artefato.
- Para sincronizar o IC de teste pipeline para executar DEPOIS do Build CI pipeline, nós costumavamos fluxo de trabalho_executar desencadear.
Chamamos isso de Cenário #3.
Embora, como mencionamos naquele post, existam outras soluções, decidimos implementar esta “solução” por razões pedagógicas, para que possamos aprofundar Veja maisto vulnerabilidades de CI/CD pipelines.
Depois, vimos como hackear esse cenário envenenando o artefato. Este é o que chamamos Envenenamento por artefato, ou seja, a capacidade de modificar (hackear) o pipeline lógica modificando um pipeline artefato.
Qual é o problema com esta abordagem? Vimos que o problema surge quando algum usuário “cria” um novo pipeline.
Se um usuário abrir um PR contendo um novo pipeline, o GitHub executará isso pipeline (dadas algumas condições, como vimos no post).
O usuário pode então criar um novo pipeline com o mesmo nome de Build CI !! Sim, é surpreendente, mas o GitHub permite criar dois pipelinetem o mesmo nome!!
Quando o usuário abre um PR com essas alterações, o novo" pipeline será executado (carregando um artefato envenenado) e o Implantar IC pipeline será executado depois disso, resultando no script de shell “modificado” substituindo o script de shell “original” localizado no pipeline área de trabalho. Portanto, esta “solução” não evita a vulnerabilidade do EPI (como podemos ver abaixo)
Quais são os problemas? Pelo menos, existem alguns problemas:
- Primeiro, como ter certeza de que o processo de construção não foi adulterado? Neste cenário, o usuário mal-intencionado conseguiu modificar o processo de construção pretendido usando seu pipeline para criar um artefato envenenado.
- Em segundo lugar, como podemos avaliar a proveniência de um artefato?
Estas questões nos jogam nos braços do Atestados de Software domínio!!
Atestados de Software
An atestado é um pedaço de dados, representando prova de um evento. No mundo real, geralmente chamamos isso de certificações.
Por exemplo, quando um laboratório analisa o seu sangue, os dados sobre o teste são registrados e certificados. Os resultados dos exames de sangue são verificável e rastreável.
Mais perto do nosso domínio de TI, você poderia adivinhar o que seria uma tradução desse processo para, por exemplo, um processo de compilação.
Informações sobre o ambiente e as ferramentas do servidor de compilação, os materiais (o código-fonte) e os Produtos/artefatos (o código binário) fariam parte dessa atestação.
Obviamente, o atestado deve ser gerado por um atestador autorizado (autenticado e não repudiável) para conferir credibilidade.
Provavelmente, alguns de vocês podem estar pensando.. e qual é o diferença entre assinaturas e atestados?
Assinaturas e Atestados de Código
Em alto nível, um assinatura é criado usando um par de chaves e um artefato. O par de chaves consiste em uma chave pública e uma chave privada.
O usuário assina um artefato usando a chave privada e outros podem então verificar a assinatura usando a chave pública. A chave privada deve ser mantida em segredo, mas a chave pública é amplamente distribuída.
Assinaturas podem ser usadas provar que o detentor da chave privada usou a chave privada para assinar o artefato.
Assinaturas não prove
- O usuário intenção para assinar o artefato (eles podem ter sido enganados), ou
- A intenção do usuário de fazer qualquer afirmação específica sobre o artefato
Com Atestados, em vez de assinar um artefato diretamente, os usuários criam algum tipo de documento que. captura sua intenção por trás da assinatura do artefato e de qualquer reivindicações específicas sendo feito como parte desta assinatura.
Estrutura de atestado in-toto
O quadro mais comum é o Estrutura de atestado in-toto
- define um standard formato para atestados que vinculam assuntos, os artefatos sendo descritos, a metadados autenticados sobre o artefato
- fornece um conjunto de predicados predefinidos para comunicar metadados autenticados em todas as cadeias de fornecimento de software
Vamos entrar em alguns detalhes sobre o formato do atestado.
An Atestado é um documento assinado digitalmente Isso contém Afirmações.
O processo de Declaração é a camada intermediária do atestado, vinculando-o a um determinado Assunto e identificar inequivocamente os tipos de Predicado:
- Assunto: Para referência criptograficamente segura ao artefato (geralmente por meio de um hash) e
- Predicados: um conjunto de específicos reivindicações sobre esse artefato é chamada de Declaração. Essas afirmações podem ser usadas para expressar (e mais tarde provar) qualquer coisa que você possa imaginar! Eles podem representar aprovação manual, origem do artefato, resultados de testes automatizados, trilha de auditoria ou muito mais!
Quando esta Declaração é assinada criptograficamente, ela é então chamada de Atestado
Desta forma, por exemplo, Alice cria uma Declaração sobre um artefato e a assina utilizando sua chave privada, criando um Atestado.
- Bob pode então verificar a assinatura naquele Atestado, permitindo-lhe confiar nas reivindicações interior.
Bob pode então usar essas afirmações decidir permitir ou não que este artefato seja usado.
Os atestados poderiam ajudar a resolver o envenenamento por artefatos?
Após esta introdução aos Atestados, vamos voltar ao nosso problema. Como podem os atestados ajudar-nos a resolver o nosso problema, ou seja, a evitar o envenenamento por artefactos?
O usuário malicioso foi capaz de criar um artefato contornando o mecanismo “oficial”, ou seja, usando seu pipeline para gerar o artefato.
Seria incrível se pudéssemos provar que os artefatos baixados foram construídos com o software oficial pipelineS. Este é apenas um exemplo do que poderíamos chamar de “pontos de adulteração”, mas pode haver muitos outros.
Como você pode ver na imagem acima, os pontos de violação são múltiplos. Desta forma, o consumidor pipeline (Teste CI em nosso exemplo) deve avaliar o integridade do processo de construção bem como o integridade do próprio artefato.
Em nosso exemplo, o artefato envenenado foi criado pela introdução de um novo artefato (envenenado) pipeline que interfere no processo de construção. Mas o usuário mal-intencionado poderia ter conseguido:
- modificar o código após ser retirado do SCM para gerar um binário malicioso
- substitua o binário correto produzido pela compilação por qualquer outro binário malicioso
- comprometer o Artifact Registry e fazer upload de um artefato envenenado construído de qualquer outra forma
- etc.
Como você pode ver, pode haver vários pontos de “adulteração”.
O que é importante aqui? Obviamente para proteger todos esses pontos de “adulteração”. Mas, no final das contas, o que é mais importante é que o “consumidor” do artefato possa avaliar a integridade do artefato e decidir se deve prosseguir ou não com ele.
Nós podemos avaliar a integridade de um artefato de duas maneiras.
Uma delas é avaliar o proveniência do artefato.
Ao gerar um Atestado de Proveniência, fornecemos metadados úteis (devidamente autenticados e não repudiados) sobre o artefato. Nos exemplos a seguir usaremos SAL Xygeni (Software Attestations Layer for Trust), o componente para gerar, registrar e verificar atestados de software.
- name: Building ...
run: |
# mvn will compile and create target/MyApp.war
mvn clean package
- name: Generating provenance
run: |
#!/usr/bin/env bash
shopt -s expand_aliases
alias salt=$PWD/salt_pro/xygeni_salt/salt
echo " "
echo "-----------"
echo "Generating Provenance with CLI ..."
salt at slsa \
--basedir ${GITHUB_WORKSPACE}/target \
--key="${PRIVATE_KEY}" \
--public-key=${GITHUB_WORKSPACE}/Test1_public.pem \
--key-password=${KEY_PASSWD} \
--output-unsigned=${GITHUB_WORKSPACE}/cli_provenance_${PIPELINE}_unsigned.json \
--pipeline ${PIPELINE} --pretty-print \
--file ./MyApp.war
No código acima, você pode ver que há uma etapa que cria o arquivo war e uma segunda etapa que gera o arquivo war. Atestado de Proveniência. Para fazer isso, o pipeline usa a chave privada e também inclui a chave pública no atestado.
Nos bastidores, Xygeni sal comando armazena o atestado em um livro-razão (também conhecido como registro de atestado, registro no nosso caso, mas você pode usar qualquer outro). Feito isso, o consumidor pipeline pode incluir Xygeni Mecanismo de verificação para verificar a procedência do artefato e avaliar a integridade do artefato.
- name: 'Verifying the attestation'
run: |
#!/usr/bin/bash
echo " "
echo "-------"
# Calculate sha256sum for the artifact
SHA_SUM=$(sha256sum ./MyApp.war | cut -f1 -d ' ')
# Recover the attestation Id from the sha256sum
ATT_ID=$(echo $(salt -q registry search --digest sha256:$SHA_SUM --format json) | jq -r .[-1].gitoidSha256)
echo " "
echo "-------"
# Download the provenance attestation
echo "Downloading the provenance attestation ..."
salt -q reg get --id=$ATT_ID --format=json > ${GITHUB_WORKSPACE}/provenance_kk.signed.json
echo " "
echo "-------"
echo "Verifying provenance ..."
salt verify \
--basedir ${GITHUB_WORKSPACE} \
--attestation=${GITHUB_WORKSPACE}/provenance_kk.signed.json \
--public-key=${GITHUB_WORKSPACE}/Test1_public.pem \
--file ./MyApp.war
O processo de verificação avalia:
- O processo de artefato sha256sum é válido (ou seja, há um atestado sobre esse “assunto”), e
- O processo de o atestado está devidamente autenticado (foi gerado usando a chave privada adequada)
Este processo de verificação pode avaliar se tanto o artefato quanto o atestado são válidos.
Mas, como você lembra, no nosso caso, o artefato foi gerado por um dispositivo “malicioso” pipeline (ou seja, não o original por um modificado pipeline). Então devemos ir em frente e verificar outro aspecto: que o artefato foi gerado pelo “original” pipeline, e não qualquer outro.
Para fazer isso, basta incluir uma linha simples para verificar essa condição, por exemplo:
echo " "
echo "-------"
# Download the provenance attestation
echo "Downloading the provenance attestation ..."
salt -q reg get --id=$ATT_ID --format=json > ${GITHUB_WORKSPACE}/provenance_kk.signed.json
WFR=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json |base64 -d | jq -r .predicate.buildDefinition.internalParameters.environment.GITHUB_WORKFLOW_REF)
echo $WFR | grep cicd_top10_3_salt\/.github\/workflows\/build.yml
Esta verificação adicional falhará se o artefato não tiver sido gerado pelo nosso “seguro” pipeline.
Se o artefato foi gerado pelo nosso original pipeline (cicd_top10_3_salt/.github/workflows/build.yml), o comando grep será bem-sucedido, caso contrário, falhará, quebrando o pipeline e abortar quaisquer etapas adicionais.
O construtor" pipeline é apenas um ponto de violação a ser verificado, mas, como mencionado anteriormente, há alguns outros pontos de violação que devemos verificar.
Por exemplo, nos e se o código-fonte tiver sido adulterado após a verificação do repositório e antes do comando de construção? Neste caso, o código a ser construído não é o mesmo que o armazenado no SCM.
Verificar esse ponto de adulteração é tão fácil quanto verificar os hashes do material a cada passo.
SHA_ATT_MATERIAL=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json | base64 -d | jq -r .predicate.attestations[0].predicate.materials[].digest[])
SHA_STEP_MATERIAL=$(jq -r .payload ${GITHUB_WORKSPACE}/provenance_kk.signed.json | base64 -d | jq -r .predicate.attestations[3].predicate.materials[0].digest[])
Conclusões
Em resumo, um Atestado de Software é uma afirmação feita sobre um software, ou seja, uma declaração autenticada (metadados) sobre um artefato de software ou uma coleção de artefatos de software.
Os atestados de software são uma generalização da assinatura bruta de artefato/código. O atestado é um documento assinado (em um determinado formato, normalmente baseado em JSON) que associa metadados a um artefato. Eles representam evidências que ligam entradas (materiais) e saídas (artefatos produzidos) em cada etapa da construção.
Os atestados fornecem um registro verificável das etapas realizadas para construir os artefatos de software finais, incluindo materiais de entrada para cada etapa e a execução dos comandos de construção.
Concluindo, os Atestados de Software são um ótimo mecanismo para verificar muitos aspectos diferentes de integridade do nosso processo de construção.
Terminou a série? Não se preocupe! Sinta-se à vontade para voltar para 'Envenenado Pipeline Execução (EPI)' ou qualquer outro post que desperte seu interesse novamente!
Fique ligado, vamos aprofundar Veja mais software atestados e build security em postagens futuras do blog.





