Usar o Jenkins já não é nenhuma novidade. É comum vê-lo em uso até mesmo em projetos que não fazem integração contínua, funcionando como um executor de tarefas agendadas. Usar Docker também não é nenhuma novidade. Ele chegou para ficar, e já não dá mais para viver sem ele <3
E como este ambiente costuma ser? Normalmente vemos uma máquina média ou grande com Docker instalado, na qual são executados ao mesmo tempo vários contêineres de serviços como Jenkins, Nexus, Gitlab, Apiary, Swagger, SonarQube, etc.
O Jenkins disponibiliza uma matriz de usuários com permissões bem fáceis de configurar e restringir. Mas você já parou para analisar a segurança da sua instalação de Jenkins como um todo, desde a máquina na qual ele está instalado?
Para nosso cenário fictício, vamos simular uma máquina Linux com Docker, na qual há vários contêineres (entre eles o Jenkins) executando.
Nosso Jenkins está protegido com uma senha bastante segura, que não possuímos. A única coisa que temos no momento é acesso SSH à máquina Docker na qual o contêiner Jenkins foi iniciado.
Vamos lá?
Acessando o nosso Jenkins, damos de cara com a tela pedindo login e senha:
Sem chance. Poderíamos fazer aqui algum tipo de ataque de força bruta baseado em uma lista de usuários e senhas comuns, mas não vamos por este caminho.
Que tal acessar o servidor? Após fazer o login, vamos acessar a pasta que foi
configurada como persistência local do Jenkins, que geralmente é montada
no contêiner como /var/jenkins_home
.
Para saber a localização desse diretório, temos que descobrir o nome do contêiner do Jenkins e depois inspecioná-lo, buscando seus pontos de montagem:
Uau! O Jenkins cria vários arquivos e diretórios. Tem coisas muito importantes nesta pasta. Vamos iniciar conseguindo o login do próprio Jenkins, depois poderemos entrar na interface e obter outros logins e senhas.
Enumerar os usuários é o primeiro passo. Basta dar um ls
na pasta users
e
pronto, já sabemos os logins que vamos atacar. Inclusive, temos acesso
ao hash da senha acessando seu arquivo config.xml
:
Ahhh, que pena! A senha é armazenada em hash JBCrypt. Isso quer dizer que não é reversível, mas não vamos desistir. Temos que entrar na interface web. Já que não sabemos qual senha gerou este hash, podemos colocar no lugar um hash conhecido, gerado por uma senha bem fácil.
Edite o arquivo, duplique e comente a linha da senha, para que tenhamos um recovery depois de conseguir acesso. Troque o hash da linha copiada para:
#jbcrypt:$2a$10$razd3L1aXndFfBNHO95aj.IVrFydsxkcQCcLmujmFQzll3hcUrY7S
Fica assim:
Este hash corresponde à senha test.
Depois de trocar o hash é só reiniciar o contêiner para que a correção seja
aplicada. Podemos usar um simples docker restart <nomedocontêiner>
e
logar com as credenciais admin
e senha test
.
Sucesso! Imaginando que somos crackers, para não levantar suspeitas agora
criaríamos uma conta com privilégios de administrador e voltaríamos a senha
de admin para o hash antigo, que salvamos no comentário do config.xml
.
Pronto, já temos acesso ao Jenkins. O que mais podemos descobrir?
Passeando pelas configurações em [Jenkins/Manage Jenkins/Configure System],
vemos que este servidor utiliza o plugin SSH. Podemos ver o usuário e a
senha salvos para o servidor SRV-PRODUCAO.acme.br
:
Podemos selecionar o campo da senha e clicar em inspecionar elemento, para ver no código fonte as propriedades desse campo.
Que beleza! Acabamos de descobrir que a senha de root do servidor é Acapulco1983 por meio do texto em value no código fonte.
Este Jenkins também possui configurado o plugin E-mail Notification. Será que conseguimos pegar algo aqui também?
Pegamos, mas parece que tem algo errado:
xKFNT3SYxVyFKqpXpYBZWBcKrAfRbLmWOdUE9XsCuZI=
Essa é a senha do usuário SMTP, mas está criptografada. Não vai ser tão simples quanto a senha do SSH.
Sabemos que o Jenkins usa uma chave master para criptografar uma chave local, e que usa essa chave local para criptografar todos os seus recursos. O que vamos fazer é algo bem simples: dentro do console do próprio Jenkins, vamos escrever um script groovy que vai usar as chaves para decodificar a senha.
Vamos acessar o console groovy em [Jenkins/Manage Jenkins/Script Console] e
solicitar nossa senha. Coloque seu hash na função
hudson.util.Secret.fromString
e deixe o titio Jenkins cuidar da
criptografia para você:
hudson.util.Secret.fromString('xKFNT3SYxVyFKqpXpYBZWBcKrAfRbLmWOdUE9XsCuZI=')
O resultado aparece no campo Result:
Agora sabemos que a senha do SMTP é AguaDeJamaica
.
Pois é, pessoal. Não dá para confiar nos mecanismos de proteção de senha do Jenkins. Imagina só um cenário corporativo com acesso a ambientes de produção… É grave. Mas os problemas não se restringem ao Jenkins. O Docker pode ser também um vetor de vários tipos de problemas de segurança.
Vamos imaginar um cenário com um servidor Docker com vários contêineres em execução, nos quais os usuários acessam com contas limitadas, mas sem acesso como root. O simples fato de permitir acesso ao Jenkins via Docker com um ponto de montagem habilitado pode ser criativamente explorado.
Logado como wesley
, um usuário limitado no host, mas membro do grupo
docker
, podendo executar contêineres livremente, vou iniciar uma sessão
como root
no Jenkins. Vou criar um shell usando o bash como base e
dar SUID
para ele, lembrando de deixá-lo na pasta acessível no
Docker host.
O que acontece quando executamos este shell com a opção -p
no Docker
host, logados com uma conta limitada?
Uaaaaaahahahahahahahahahahahahahahaha!!! Essa máquina foi conquistada!
E você, vai deixar seus servidores dando sopa?
O Jenkins é um CI Server bastante competente, e o Docker é o queridinho do momento. Já que não vamos deixá-los, como podemos torná-los mais seguros?
A notícia ruim é que não dá. Simplesmente não dá para ficar tranquilo. Se alguém quiser mesmo entrar no Jenkins (ou em qualquer outro servidor), tiver tempo, dinheiro e vontade, vai conseguir. Mas não estamos totalmente indefesos, podemos nos precaver de ataques mais básicos. Algumas dicas:
credentials.xml
, seria possível
descriptografar e obter todas elas!E lembre-se: seu ambiente é tão seguro quanto seu elo mais frágil. Quanto mais ferramentas e passos de complexidade forem adicionados ao CI, mais ampla será a cobertura que você precisará analisar em busca de falhas de segurança. Neste caso, falhas no SonarQube, Nexus e outros contêineres podem dar acesso aos servidores de produção. Em outras palavras, mantenha as coisas simples.
PS: A senha do admin Jenkins era a palavra cabalística Parangaricutirimirruaro.
Até o próximo post =)