Inside Jenkins Attack Framework: How Red Teams Exploit CI/CD Infrastructure
Hook
Your Jenkins server can execute arbitrary code without creating a single job that appears in the UI. This isn’t a zero-day—it’s a feature that penetration testers have weaponized into one of the most comprehensive CI/CD attack toolkits available.
Context
Jenkins powers the CI/CD pipelines of thousands of organizations, processing source code, deployment credentials, and production secrets daily. This makes it an extraordinarily high-value target for attackers. Traditional penetration testing tools treat Jenkins as just another web application, missing the nuances of its permission model, Groovy script console, job manipulation capabilities, and credential storage mechanisms. Accenture’s offensive security team built Jenkins Attack Framework (JAF) to address this gap—providing red teamers and penetration testers with a purpose-built toolkit that understands Jenkins’ architecture and exploits it systematically.
JAF emerged from real-world engagements where manual enumeration of large Jenkins deployments proved time-prohibitive. When a Jenkins instance hosts hundreds or thousands of jobs, each with multiple build histories potentially containing credentials in console output, manual review becomes infeasible. The framework automates the full attack lifecycle: reconnaissance (identifying permissions and job structures), exploitation (executing code and extracting credentials), and post-exploitation (maintaining persistence through ghost jobs). What makes JAF particularly interesting from an architectural perspective is how it leverages Jenkins’ own APIs and features—not vulnerabilities—to achieve its objectives.
Technical Insight
JAF implements a subcommand architecture where each attack vector is a discrete Python module. The framework supports three authentication methods that mirror real-world enterprise scenarios: username/password pairs, API tokens, and session cookies. The cookie authentication option is particularly noteworthy—it addresses federated Jenkins instances where traditional credentials fail, allowing testers to authenticate via browser, extract the session cookie, and pass it to JAF.
The credential dumping capabilities demonstrate JAF’s understanding of Jenkins internals. The DumpCreds command uses administrative access to execute Groovy scripts via Jenkins’ script console, directly querying the credential store. For scenarios where you lack admin rights but have job creation permissions, DumpCredsViaJob takes a different approach—creating jobs that explicitly enumerate credentials through Jenkins’ API. This dual-method approach shows how JAF adapts to different privilege levels.
One of the most technically sophisticated features is the “ghost job” capability within RunJob. Traditional Jenkins job execution leaves obvious traces in the UI—job names, build histories, console output. Ghost jobs appear to execute without showing up in the master’s job list after launch, providing stealthy persistence. Here’s how you’d deploy one:
./jaf RunJob -s https://jenkins.target.com -a admin:apitoken \
--script payload.sh --ghost --slave "linux-build-01"
On Windows slaves, this requires a compiled C++ helper binary (windows_ghost_job_helper.exe) that handles the execution mechanics—JAF ships with the source at data/cpp/windows_ghost_job_helper.cpp but requires manual compilation using Visual Studio’s cl tool. This Windows dependency is one of the few areas where JAF doesn’t provide turn-key functionality.
The ConsoleOutput command showcases JAF’s handling of large-scale data extraction. Jenkins build logs can contain gigabytes of output across thousands of builds, often including credentials accidentally echoed during deployment scripts. JAF implements multi-threaded console output dumping with configurable thread counts (default 4, adjustable via -t). It now supports both successful and failed builds, recognizing that failed deployments often expose even more debugging information:
./jaf ConsoleOutput -s https://jenkins.target.com \
-a user:password -t 8 -o extracted_logs/
This command spawns 8 concurrent threads, each requesting build console output, writing results to the specified output directory. The framework handles HTTP timeouts (30 seconds by default, configurable via -n) and appears to handle chunked responses for large console logs.
File upload capabilities demonstrate another Jenkins-specific attack vector. The UploadFile command uses chunked uploading via the Jenkins Console to handle file transfers, particularly useful for deploying larger payloads or toolsets onto Jenkins masters.
The AccessCheck command provides reconnaissance by testing credentials against common Jenkins permissions: Basic Read Access (read), Create Job Access (build), Some level of Admin Access (admin), Script Console Access (script), and Scriptler Groovy Script Plugin Access (scriptler). However, the README explicitly warns that “There are simply too many ways to restrict access in Jenkins and no API for determining granular access levels, so results are not always perfectly accurate.” A negative result should be accurate, but positive results mean the user potentially has the access and requires additional validation.
JAF’s authentication abstraction is particularly well-designed. The framework auto-detects credential types by parsing the input string, but also supports explicit type hints: {USERPASS}, {APITOKEN}, or {COOKIE}. For bulk credential testing, the -c option accepts either a file path or - for stdin, enabling integration with credential dumps from other tools:
cat harvested_creds.txt | ./jaf AccessCheck -s https://jenkins.target.com -c -
The CreateAPIToken and DeleteAPIToken commands provide API token lifecycle management—useful for establishing persistence (creating tokens for backdoor access) or cleanup (removing forensic evidence post-engagement). With administrative credentials, these commands can manage tokens for any user, not just the authenticated account.
Gotcha
JAF’s permission detection is explicitly unreliable for positive results. The AccessCheck command warns that “There are simply too many ways to restrict access in Jenkins and no API for determining granular access levels, so results are not always perfectly accurate.” You can trust a “no access” result, but “access granted” requires manual verification—a significant limitation when triaging large credential dumps.
The Windows ghost job feature has a deployment hurdle that breaks the framework’s otherwise streamlined setup. You must manually compile windows_ghost_job_helper.cpp using Visual Studio’s cl compiler and place the resulting executable in the correct data directory (data/exe/windows_ghost_job_helper.exe). This isn’t a simple gcc compilation—it requires a Windows development environment with Visual Studio installed, creating friction for penetration testers working primarily from Linux. The README notes that compilation arguments are provided in a comment at the top of the source file, but this manual step is easy to miss until you actually attempt to run a ghost job against a Windows slave.
JAF uses a static user-agent string for all requests: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36. While this helps blend in with legitimate traffic, the README makes no mention of request timing features, jitter, or rotation capabilities. For red team engagements against organizations with mature security operations centers, defenders with mature logging can potentially correlate rapid-fire API requests from the same user-agent as automated tooling.
The framework assumes you already have some level of access to Jenkins—even if just anonymous read permissions. It’s an exploitation and post-exploitation toolkit, not an initial access tool. If Jenkins is properly hardened with no anonymous access and you lack credentials, JAF offers no vulnerability exploitation capabilities for gaining that initial foothold.
Verdict
Use JAF if you’re conducting authorized penetration tests or red team engagements against organizations with Jenkins infrastructure, particularly large deployments where manual enumeration is impractical. It’s the most comprehensive Jenkins-specific exploitation toolkit available, and its multi-threaded credential extraction from console output alone justifies its use for any engagement involving more than a handful of Jenkins jobs. The ghost job persistence mechanism provides genuine post-exploitation value that’s difficult to replicate with generic tools. Also use this if you need to demonstrate business impact during Jenkins security assessments—the ability to dump all credentials and execute arbitrary commands quickly conveys risk to stakeholders. Skip JAF if you need defensive tooling (this is purely offensive), if you’re testing exclusively Windows-based Jenkins slaves without access to a Visual Studio compilation environment (ghost jobs won’t work), or if you lack any initial access to Jenkins whatsoever (this isn’t an initial access framework). For vulnerability scanning rather than exploitation, consider other tools with broader vulnerability coverage; for integration with broader post-exploitation workflows, other frameworks may offer better integration, though with less Jenkins-specific depth than JAF provides.