Recently, I discovered a sandbox breakout in the Groovy Sandbox used by the Jenkins script-security Plugin in their Pipeline Plugin for build scripts. We responsibly disclosed this vulnerability and in the current version of Jenkins it has been fixed and the according Jenkins Security Advisory 2019-09-12 has been published. In this blogpost I want to report a bit on the technical details of the vulnerability.
Description
The groovy sandbox transforms some AST nodes of the script to add security checks. For example
ret = Runtime.getRuntime().exec("id")
will be transformed to something like
ret = Sandbox.call(Sandbox.call(Runtime.class, "getRuntime", []), "exec", ["id"])
Sandbox.call
will check at runtime if the script can call the method with the given arguments.
However, there were some cases in which the transformer did not transform child expressions, which then could do anything.
1.(untransformed)()
1.(untransformed) = 1
sometimesuntransformed++
In the first case the method name of a function call was not transformed. Who thought that a function name needs to be an identifier? The second case has the same problem but for the name of a property expression as the left-hand side of an assignment. For the last case the expression needs to not be of the form of a[b]++
, a++
, a.b++
. And in a.(b)++
, b
is also not transformed.
This allowed everyone who was able to supply build scripts to execute commands as the Jenkins Master.
PoC
The script-security and pipeline plugins are required but installed by default. A user with job/configure permission is needed to change the script code. The following pipeline script will run the id
shell command and throw and error with its output.
@NonCPS def e(){ 1.({throw new Error("id".execute().text)}())(); } e();
@NonCPS
is needed to disable a transformation step that would make problems.
The expected output of this script after a build is:
As seen in the output the command did run as Jenkins without approval of an administrator.
Disclosure Timeline
We responsibly disclosed this vulnerability and in the current version of Jenkins, it has been fixed and the according Jenkins Security Advisory 2019-09-12 has been published. The disclosure timeline was as follows:
- 12.09.2019 – Public disclosure of vulnerability
- 10.09.2019 – Vulnerabilities were assigned CVE-2019-10393, CVE-2019-10394, CVE-2019-10399, CVE-2019-10400
- 06.09.2019 – Report
Conclusions
Sandboxing is hard and a little oversight (that property names can be arbitrary expressions) can lead to escapes.
I strongly recommend to update to the most current version if you use Jenkins, where this issue has been fixed and of course ensure proper patch and vulnerability management processes to be in place in general.
Best,
Nils