- 10年以上セルフホスティングしている公開IPのアクセスログには、認証情報の探索からIoT機器を狙ったコマンドインジェクションまで、インターネットに公開されたサービスが日常的に受ける攻撃の試みが表れている
- 最もよく見られたパターンは、
.env、.aws/credentials、.git/config、backup/、test/ などのパスを探る設定ファイル・ディレクトリ探索で、一部のリクエストは Mozlila/5.0 のようなスペルミスのあるUser-Agentを使っていた
- Shellshock の試行では、User-AgentにBash関数形式のペイロードを入れて
/etc/passwd を読もうとしており、複数のCGIパスを繰り返し推測していた
- LuCI と Zyxel を狙った攻撃は、ルーターや組み込み機器のWebインターフェースにコマンドを注入し、リモートスクリプトや複数アーキテクチャ向けバイナリをダウンロードして実行しようとしていた
- 公開インターネットに露出させる対象を減らし、必要なツールやディレクトリには認証とIP制限を適用し、IoT機器は最新状態に保ち、可能なら公開ネットワークから分離すべき
公開インターネットに露出したサービスが受けるトラフィック
- 10年以上セルフホスティングを続け、自分のデータを直接所有し、クラウドホスト以外のプラットフォームへの依存を減らす経験を積み重ねてきた
- 公開インターネットにIPをさらすと、すぐに大量の悪性トラフィックが入ってきて、アクセスログを見れば最近どのような攻撃を受けたか追跡できる
- この分析はセキュリティ専門家のフォレンジックというより、好奇心の強い開発者の観察に近い
- 攻撃者のIPアドレスと、一部の攻撃者が使った侮辱的な表現は、念のため伏せてある
認証情報と設定ファイルの探索
- 最も一般的な攻撃はディレクトリトラバーサルによる認証情報の探索で、とくに
.env ファイルへのリクエストが多く見られた
- リクエストパスの例は
/laravel/.env、/backend/.env、/api/.env、/.env、//.env など
.env は一般にアプリケーションのsecretを格納するファイルとして扱われる
- AWS関連ファイルやGitリポジトリの設定も探索対象に含まれていた
- 例として
/aws.yml、/.env.bak、/info.php、/.aws/credentials、/config/aws.yml、/.git/config など
- 誤って残された可能性のある一般的なディレクトリも繰り返し要求されていた
- 例として
/old/、/new/、/test/、/backup/、/temp/ など
- 一部のUser-Agentには
Mozilla/5.0 のタイプミスと思われる Mozlila/5.0 が含まれていた
- GitHub検索 の結果は、この誤記が一般的なツールで生成され、コピー&ペーストされた可能性を示唆している
- リモートアクセスツールや設定ツールを探すリクエストも確認された
- 例として
/actuator/gateway/routes、/hudson、/ui/login.action、/?XDEBUG_SESSION_START=phpstorm など
- 公開インターネットには本当に必要な最小限の項目だけを公開し、ツールやディレクトリを公開する必要があるなら、認証レイヤーと可能であれば特定IP制限を設ける必要がある
Shellshockの試行
- 複数のリクエストは Shellshock 脆弱性を狙ったものに見えた
- この攻撃は、脆弱なBashバージョンでCGIスクリプトを実行するWebサーバー上で任意コマンド実行を狙う
- CGIプログラムの起動時には、リクエスト内容から環境変数が設定され、
HTTP_USER_AGENT もその1つ
() { :; }; のような文字列が含まれると、Bashはそれを実行すべき関数として解釈する
- 実際のログのUser-Agentには、次のようなペイロードが含まれていた
() { ignored; }; echo Content-Type: text/html; echo ; /bin/cat /etc/passwd
() { ignored; }; はBash関数定義の形式
echo Content-Type: text/html; echo ; はHTTPレスポンスのContent-Typeと空行を出力する
/bin/cat /etc/passwd は、ユーザーアカウント情報が入った /etc/passwd の内容を出力しようとするコマンド
- 攻撃が成功していれば、ユーザー認証情報へのアクセスやサーバー上での任意コード実行につながり得た
- 攻撃者はディレクトリトラバーサルと同様に、
/cgi-bin/status、/cgi-bin/stats、/cgi-bin/test、/cgi-bin/status/status.cgi、/test.cgi、/debug.cgi、/cgi-bin/test-cgi といった一般的なパスを推測していた
LuCIを狙ったコマンドインジェクション
- あるリクエストは、OpenWRTルーター向けWebインターフェース LuCI を狙ったものと見られる
- 攻撃URLは
country フィールドにコマンドを注入し、リモートサーバー上のシェルスクリプト tenda.sh をダウンロードして実行しようとする構造だった
- URLデコード後のコマンドは、
/tmp に移動し、ファイルを削除し、wget でスクリプトを取得し、実行権限を付与してから実行する流れ
- ダウンロードされたスクリプトには、追加バイナリをダウンロードして実行しようとする内容が含まれていた
- 対象アーキテクチャ名と思われる
mips、mpsl、x86_64、arm、arm5、arm6、arm7、i586、i686、powerpc、sh4、m68k、sparc などが含まれていた
- 複数アーキテクチャ向けバイナリを試す方式は、攻撃者が対象機器のアーキテクチャを把握していない状況で攻撃範囲を広げようとする構成に見える
- 追加調査のため、環境と互換性のないバイナリをダウンロードし、Ghidra で確認した
- 当初は関数が3つしかなく、文字列データもあまりなかった
- 大きなデータ領域から
$Info: This file is packed with the UPX executable packer と UPX 3.94 という文字列が確認された
- UPX で圧縮されたELFバイナリで、UPXヘッダーやpacked binaryが改変されていなければ
upx -d で展開できる
- 実際に
upx -d mips を実行すると、ファイルサイズは 34932 から 93732 に増え、より多くの文字列を確認できた
- 展開後バイナリの文字列には
M-SEARCH * HTTP/1.1、ST: urn:dial-multiscreen-org:service:dial:1、Huawei機器のアップグレード向けXMLペイロードが含まれていた
- これは、ネットワーク上でDIALプロトコルをサポートする機器を探すUPnPコマンドに見える
- XMLペイロードは、コマンドインジェクションに脆弱なHuawei機器をスキャンするよう構成されているようだ
- この挙動はMirai botnetの一部として識別されたものに見える
- 参照されていた
yeye.mips ファイルは、取得しようとした時点では利用できなかった
- サーバーに
nmap を実行した結果、開いていたポートは 22/tcp ssh と 646/tcp filtered ldp だけが確認された
Zyxelを狙ったコマンドインジェクション
- 別のリクエストはGET URL内にシェルコマンドを含んでおり、
${IFS} シェル置換を除去すると読みやすい形になった
- 整理したコマンドは、
/tmp に移動し、*mips* ファイルを削除し、huhu.mips をダウンロードして実行権限を与えた後、zyxel.selfrep 引数付きで実行する流れ
- この攻撃は Zyxel 機器の
zhttpd エクスプロイトを狙ったものと見られる
- 今回のバイナリはpacked状態ではなかったため、Ghidraで文字列をそのまま確認できた
- 文字列には
M-SEARCH * HTTP/1.1、DIAL関連の ST ヘッダー、skyljne.arm、skyljne.arm5、skyljne.arm6、skyljne.arm7、skyljne.mips、skyljne.mpsl、skyljne.x86_64、skyljne.sh4 などが見られた
- Huawei機器を対象に
huhu.mips をダウンロードし、selfrep.huawei として実行するXMLペイロードも含まれていた
- 別の文字列には
/goform/set_LimitClient_cfg にPOSTを送るコマンドが含まれていた
Cookie: user=admin ヘッダーとともに mac パラメータへコマンドを注入し、huhu.mpsl をダウンロードして実行しようとしていた
- Akamaiの記事 によれば、この脆弱性はルーターを対象とし、特別な認証・認可チェックがないため事前認証なしで悪用可能とされる
- このバイナリはMirai botnetのエージェントである可能性が高いと判断された
- 一部の情報源ではLinux Medusaとの関連の可能性にも触れている
運用面での対応
- 確認できたログは全体の一部にすぎず、日々ほかにも多くのエクスプロイトが試みられている
- 機器、特にIoT機器を最新の状態に保つことが重要
- 可能であればIoT機器を公開インターネットへ直接露出させるべきではない
- どうしても公開する必要がある場合は、できる限り別のVLANに隔離すべき
1件のコメント
Hacker News のコメント
興味深いことに、一部の攻撃者は新しく発行された証明書を見つけようとして Certificate Transparency ログを監視しているようです。
新しいサーバーを新しい IP で1週間以上立ち上げておくと、アクセスログにはランダムな探索が数件見えるだけなのに、Let’s Encrypt の証明書を取得してから1時間ほど経つと、記事に出てくるようなリクエストが数百件単位で押し寄せたことが何度もありました。
結論として、新しいサービスはできるだけ早く、理想的にはインターネットに公開する前から安全にしておくべきだということです。
あるいは独自の認証局を使い、切り替え前までは自己署名証明書と mTLS を使う方法もあります。
たとえば、あるソフトウェアが管理者アカウントの作成や DB 接続といった初期セットアップ画面をそのまま露出する方式だと、環境変数・設定ファイル・専用のシークレット管理ツールで最初から指定する方式より危険になります。
たとえば、特定期間内のドメインを検索するようなものです。
Cloudflare の Merkle Town[0] は概要を見るのに便利ですが、CT ログを簡単にクエリする方法はまだ見つけられておらず、ct-woodpecker[1] も有望に見えます。
[0] https://ct.cloudflare.com/
[1] https://github.com/letsencrypt/ct-woodpecker
最近の VPN は非常に良くなっていて安心できますし、写真ホスティングやバックアップのようなものは特に公開したくありません。
自前でホスティングしたサイトを管理し始めた頃はアクセスログも見ていましたし、しばらくはデータを集めて入ってくる攻撃の試みを表示してくれる侵入検知システムも使っていました。
結局、ログを先回りして確認することも、侵入検知システムに費用を払うこともやめました。時間の無駄で、気を散らすだけでした。
よくある脆弱性や攻撃をうまくまとめた資料は簡単に見つかるので、それをサーバー管理の基準にすれば十分です。一般的な Web サーバー技術ごとにベストプラクティスのガイドはたくさんあり、それを100%実行するだけで、ほぼすべての攻撃者よりはるかに先んじることができます。
次に時間とリソースを有効に使う方法は、可能な限り速いパッチ適用サイクルを優先することです。攻撃の大半は公開済みの脆弱性を狙います。
ログが特に役立つのは、問題が発生した後の診断時です。ログ分析ソフトウェアで保存・検索し、成功した攻撃の根本原因を見つけるのに2〜3回役立ちましたが、毎回、パッチ適用が遅すぎた既知の脆弱性が原因でした。
解決策は多層防御で、個人向けサービスを自分でホスティングする場合は、たいていかなり簡単に適用できます。
前段にファイアウォールを置く、あるいは VPN/Tailscale の背後に隠し、自動化攻撃が狙う
/phpmyadmin/ではなく/mawer/phpmyadmin/のようなサブフォルダに隠せば、99.9% は見つけられません。セキュリティ・バイ・オブスキュリティと呼ばれるもので、これだけを信じてはいけませんが、追加の層としては非常に有用です。アプリをサンドボックスに入れ、サーバーを隔離して、侵入されても他へ移動しにくくし、ログを残して攻撃の有無と成功の有無を確認できるようにすべきです。
要点は、パッチであれファイアウォールであれ、単一の防御手段だけに頼ってはいけないということです。結局どれか一つは失敗します。
今は四半期ごとにパッケージを更新しようと試みています[0]が、既知の脆弱性を知らせてくれて即座に対応できるツールがあるとよいです。
[0] ここで「試みる」とは、最新バージョンに適用しにくい互換性の破壊があったり、まだ信頼していない X.0.0 リリースだったりする場合には、すぐにはアップグレードできないという意味です。
投稿者はセキュリティ専門家ではないと言っているので細かい点を直すなら、前半の例は認証情報・設定の探索であって、ディレクトリトラバーサルではありません。
ディレクトリトラバーサルとは、攻撃者が Web ルートから「脱出」したり、サーバーに通常のディレクトリ外のファイルを提供させるようだましたりする手法に使う表現だと理解しています。
"/../../passwd/etc"のような場合です。少なくとも私の経験では、こうした攻撃のかなりの割合が敵対的な国家主体から来ている点が重要です。
議論を呼ぶでしょうが、取引することのない問題国の IP レンジ全体をブロックするのは有用な場合があります。これによって、新しいサービス1つに来ていた探索の試みを100%止められたことがあります。
私のサーバーに来る探索のほとんどは米国からで、かなり離れて2位がオランダです。おそらく大半は AWS や他のデータセンターから来ているのだと思います。
アプリケーション/製品セキュリティ分野で働いており、数十億ドル規模の企業の WAF を何年も管理してきた
DNS を Cloudflare に移し、サイトに WAF ルールをいくつか置けばよい。たとえばボットスコアが 2 未満ならマネージドチャレンジを出す、攻撃スコアが特定の値のときに処理する、といった具合だ。費用もほとんどかからない可能性が高く、多くの問題が解決するはず
ただし本番に移す前に必ずテストすべき。テスト用ドメインを 1 つ用意するのもよい。WAF は万能の解決策ではなく、応急処置に近い。アプリ自体が攻撃に耐えられるよう強化されていなければ、どれほど高度な WAF やボット対策を使っても救えない
Free Managed Ruleset はデフォルトで配布されているようで、Cloudflare は変更ログをここで管理している: https://developers.cloudflare.com/waf/change-log
とてもよく機能している。アクセスするのが家族だけなので設定も簡単だったし、それぞれ固有の証明書を受け取り、必要なら失効させられる
この分野に詳しそうなので気になるのだが、Azure インフラと Cloudflare を併用するソリューションも管理したことがあるか、もしあるなら OWASP のような一般的なもの以外で、人々がよく見落とす点があるか知りたい
何らかの理由で、ある会社が最新ではないものをどうしても運用しなければならないなら必要かもしれないが、結局は応急処置にすぎない
自分で設計した 400 行の HTTP/S サーバーを 1 年ほどセルフホスティングしているが、開いている 3 つのポート(22、80、443)に入ってくる 攻撃トラフィックの多さには驚く
ただ、攻撃者が実際に何をしようとしているのかは分析する時間を取っていなかったので、この記事が多くの空白を埋めてくれた
/var/log/auth.logに見える変なものも同じやり方で分析できるとよさそうコードはすべてオープンソースで、サーバー側の状態もないのに、攻撃者がわざわざ自分を狙うのは不思議だ。攻撃者が得られる最善のものは、月 5 ドルの VPS の root アクセス権と、誰も来ないドメインの一時的な改ざんくらいだ
最もよく知られた 3 つのポートを開ければ接続は入ってくるし、君が何を動かしているかなど知らないし気にもしていない
マルウェアのホスティングや 暗号資産マイナーの実行も可能だ
私の ISP は実際には IP をほとんど変えないし、変わったらホスティングの Web 管理パネルにログインしてルールを更新できる
サーバーごとに fail2ban は必ず動かし、公開しているサイト機能の種類に合わせた攻撃を捕まえるためにカスタム jail も追加している
ただ、fail2ban のほかのデフォルト値が、今でも一般的な攻撃を防ぐのに十分かは長いこと確認していない。あとで見られるよう、このリンクをブックマークしておくべきだな
セルフホストしたサービスのアクセスログは自分も確認しているが、この分析で目立って抜けている細部がある
悪性リクエストのかなりの部分は、GitHub などで簡単に入手できるセキュリティスキャナーを一般人が走らせているものだ。たいてい洗練されていない攻撃で、こうしたプロジェクトのインスタンスが、応答を見もせず、制限にかかったかも気にせずにサーバーを叩くというものだ
攻撃の中には IP を直接狙うのではなく、ドメインとサブドメインを監視し、定期的に同じ IP レンジから同一のスキャンを繰り返すものもある
以前の職場では、トルコの単一の固定 IP から繰り返しスキャンが入り続け、チームではその人を「トルコ人」と呼び始め、奇妙なリクエストパターンを見ると、まずその人が私たちのサービスに触っているか確認する段階がインシデント対応に組み込まれた
AWS でこうしたログを見ているなら、ぜひ VPC の前に AWS WAF を置いたほうがよい
高くはなく、こういう状況で厄介ごとをかなり減らすのにけっこう役立つ。サービスまで到達するすべてを止められなくても、大きな助けにはなり得る
過剰に動作するルールがいくつかあり、アプリの一部をひそかに壊していた
リクエスト本文に
"localhost"が入っているとブロックするリクエスト本文ルールがあり、User-Agent ヘッダーのないリクエストをブロックするルールもあった。以前は API リクエストに User-Agent を要求していなかったため、原因を見つけるまで一部ユーザーの API 全体を壊していた何がブロックされるのかを把握し、先に検証すべきで、そうしないと場合によっては顧客を失う
簡単に回避されるのにセキュアになったという錯覚を与え、性能コストも大きく、正常なトラフィックをブロックする可能性もかなりある: https://www.macchaffee.com/blog/2023/wafs/
たとえば私たちの大学の研究者は Twitter データを研究しているが、ツイートの小さなランダムサンプルからリンクをたどるという理由だけで、大学の IP がほとんどの WAF にブロックされる
こうした攻撃の一つにきちんと応答する Express サーバーを作って、誰かの時間を無駄にさせたら面白そうだと思うことがある
ただ、そうすると自分の時間も無駄になる
[1] https://infosec.exchange/@gnyman/109318464878274206
[2] https://nyman.re/super-simple-ssh-tarpit/