What is Jenkins Shared Library ?
Jenkins shared library is popular where large number of jenkins jobs or pipelines uses a repeated code in pipeline script. The developers creates certain modular functions containing the repetitive code and then reuses across various projects/pipelines/jobs. It is commonly seen in enterprise or organizations where teams work on multiple projects that shares common patterns in every pipelines. Imagine a function to send notifications to Slack about a build passed/failed can be used by many jobs.
These shared functions are often kept in some SCM like github and may or maynot be publicly accessible. As they are helper functions and does not carry any customer data/code, the scm repository is usually not private sometimes.
What does the project structure of Jenkins Shared Library looks like?
(root)
+- src # Groovy source files
| +- org
| +- foo
| +- Bar.groovy # for org.foo.Bar class
+- vars
| +- foo.groovy # for global 'foo' variable
| +- foo.txt # help for 'foo' variable
+- resources # resource files (external libraries only)
| +- org
| +- foo
| +- bar.json # static helper data for org.foo.Bar
Most of the groovy code for these wrappers are usually placed in the vars directory. Hence if you write some wrapper code, you would place it in vars directory
You can have a look at this repository for reference https://github.com/darinpope/github-api-global-lib
Note : As an attacker if you find any codebase or SCM repository of the similar pattern, then you can confirm that you have found a Jenkins Shared Library.
Why as an attacker this could be interesting to us ?
- We can exfiltrate data from each of the build environment.
- We can inject malicious build pipeline to put a backdoor in the artifacts.
- Most importantly, we can compromise on large scale and remain stealthy, depending on how many jobs are executing the shared-library in their build.
Attack Methodologies
Attack Methodology 1 : By directly modifying the Shared Library Code
Imagine you got some credentials or access tokens that gave you access to SCM and then you got write access to a SCM repository containing the shared library code. You can navigate inside the vars directory and backdoor the groovy code from the existing library. Here for example I have added little exfiltration to helloWorld function call.
This is the original code
def call(Map config = [:]) {
sh "echo Hello ${config.name}. Today is ${config.dayOfWeek}."
}
This is the backdoored code
We need to ensure the backdoor code fulfils some properties.
- The backdoor code should not break the pipeline ( should always be successful ).
- Should leave as little footprint as possible.
Attack Methodology 2 : By changing the shared library location
Assume you got access to Jenkins console by some exploit or credentials. If you can see some build logs and have enough access to update configuration this method will still work. Remember, sometimes you can still get access rights to update configuration inspite of not being an Administrator.
At first we will try to find the location of the shared library from the build logs.
Assume in this case you don't have write access to the SCM repository but you can view the code in the SCM repository. In such case what you can do is,
Create a new SCM repo, copy the code, modify the code and then update the shared library location in Jenkins with the new repository link and branch name.
Manage Jenkins -> Configure System -> Global Pipeline Libraries
Analyzing the results.
Here is the pipeline script of Job 1 project
Here is the pipeline script of Job 2 project
Now we can review our request catcher output window
We found the requests initiated by the curl and exfiltrated the environment variables. We can decode the base64 values and get the data.
What else can we do ?
Say for example we have a Java Enterprise Project. We can modify the Shared Library to add some pipeline script to download some JSP backdoor and add them to the project locations. In this way we will get a backdoored artifact.
What can be done for prevention?
- All users should not have roles to change the configuration of the Shared Library Location.
- Maintain logs and audit trails and trigger alert whenever there is a change in the Shared Library Location placeholder.
- https://github.com/darinpope/github-api-global-lib
- https://www.jenkins.io/doc/book/pipeline/shared-libraries/