はじめに
この記事では、DNS増幅攻撃をフィルタリングする方法、およびそのアイデアを実装するために書かれた小さなモジュールについて、かなり明白であると思われます。
このようなdns増幅攻撃は、たとえば
ここに複数回記述されてい
ます 。 多くの人がこれに直面し、多くの人が戦い、より成功した人もいれば、そうでない人もいた。 この攻撃は、送信元IPアドレスが被害者のIPアドレスに設定されたDNSサーバーにDNSクエリを送信することに基づいています。 DNSサーバーからの応答は、ほとんどの場合、要求よりも大きくなります。特に、攻撃者は通常、任意の要求を実行することを考慮してください。 AAAAレコーディングはもはや珍しくありません。TXTレコーディングのSPFおよびその他の情報により、5回以上の増幅が非常に簡単になります。 攻撃者にとって、これは非常に魅力的で、大規模なボットネットがなくても、適切な処理を行うことができます。 インターネット上でIPアドレススプーフィングが可能な理由は非常に長い間議論できますが、現実にはそれが可能であるため、今日では、このような攻撃を実行する際に独自のDNSサーバーを使用することを困難にするタスクが非常に重要であるように思われます。 また、この攻撃では、権限のあるDNSサーバーとパブリックリゾルバーの両方を使用できることにも注意してください。 提案されたソリューションは、どちらの場合にも使用できます。
DNSサーバーに適用される主な闘争方法:
不要なものをブロックします。
原則として、ここでは難しいことは何もありません。多くのファイアウォールは、1秒あたりに通過するパケット数を超えたときにトラフィックをブロックする機能を備えています。 たとえば、上記の記事で行われたように、任意のルールでブロックできます。 DNSサーバーでファイアウォールを使用したくない場合は、特定の期間に1回tcpdumpを実行し、その出力を解析し、ルーティングによって不要なトラフィックを/ dev / nullにルーティングできます。 極端な場合には、攻撃者のIPをループバックインターフェイスに追加できます(この手法は、FreeBSDでファイアウォールを使用しない方法として、会議の1つでI. Sysoevによって推奨されました)。 スイッチでトラフィックミラーリングを構成し、それを別の場所で分析し、結果をエッジルーターに送信してブロックできます。 多くのオプションがありますが、マイナス1つです。トラフィックの一部が失われています。 置換されたIPをブロックしていることを忘れないでください。DNSサーバープロバイダーから独自の組織のサーバーまで、あらゆるものが存在する可能性があります。TCPを要求しましょう。
パケットヘッダーDNSにはTCフラグフィールドがあります。 TCフラグが設定されている場合、クライアントはTCPを使用して要求を再試行する必要があり、他のすべての応答データは無視されます。 この方法の考え方は、攻撃者がTCPに切り替えず、意味がないことであり、正直なクライアントはTCP経由で切り替えて応答を受信するというものです。 もちろん、DNSのTCPは遅いですが、最初に、答えは再帰またはクライアントのキャッシュに落ち着く必要があります。次に、この場合の遅延はそれほど悪ではありません。 このアプローチは、いくつかのDNSサーバーで既に実装されています。たとえば、powerdnsでは、tcフラグを設定して任意の要求に応答できますが、これはすでに適切な妥協策です。 しかし、このオプションも理想的ではありません。 事実、RFCに完全に準拠していないか、単に誤って構成されているサーバーがビッグインターネット上にまだあり、すべての回答にTCを設定するだけでは、全員がTCPを介して回答を正しく再送信することを保証しません。 また、tcフラグを設定することにより、当然、発信トラフィックの量、したがってネットワーク機器の負荷を減らすことを忘れないでください。ただし、ここではサーバー自体がこの巨大な着信要求ストリームを処理し、貴重なコンテキストスイッチを費やし、データセンターを温めます。
実際、アイデアは新しいことをするために思いつきました。 ただし、原則として、DOSに対する理想的な保護手段はなく、提案されたオプションにも欠点がないわけではありません。たとえば、サーバーを使用してdnsトラフィックを増幅せずに反映することは保護されません。 ただし、提案されたソリューションの利点は次のとおりです。
- 誰もブロックしません。しきい値を超えた場合、特定のIPアドレスに対してtcpの使用を強制します。つまり、tcフラグを設定し、切り捨てられたdns応答で応答します。
- TCPの使用の強制は、カーネルで実行されます。
また、iptablesを使用したり、追加のルールを設定したりする必要なく、使用に関してソリューションをできる限りシンプルにしたかったのです。このモジュールはLinux固有ですが、FreeBSDでの実装を根本的に妨げるものはないと思います。
どのように機能しますか?
一定期間内の各IPアドレスから受信したパケットの数を考慮します。 特定のIPから受信したパケットの数がしきい値を超えた場合、TCフラグを使用してUDP応答を形成し、要求を破棄します。 したがって、このトラフィックをDNSサーバーアプリケーションで処理する必要があるために発生するコンテキストスイッチの数を大幅に削減します。 tcフラグ付きのudp応答を受信した正当なクライアントは、TCPを介して要求を繰り返すことを強制され、このトラフィックはすでにDNSサーバーに到達します。
効果的な実装のために、dns要求と応答のヘッダー形式が同じであるという事実は、多くの場合に役立ちます。さらに、dns応答が正しいと見なされるように、ヘッダーはパッケージの唯一の必要な部分です。 dnsヘッダーをさらに詳しく見てみましょう。
別の良いニュース:dnsヘッダーのサイズは12バイトに固定されています。 非常に単純なスキームであることがわかり、dnsヘッダーを完全に解析する必要さえありません。 53 UDPポートに到着したパケットに12バイトを超えるデータが含まれていることを確認し、最初の12バイトのデータをコピーします(書き込み中に、おそらく残りのヘッダーフィールドを追加で確認する必要があるという考えが現れた)リクエストから新しいパケットにTCビットを設定しますそして応答ビット、そしてそれを送り返します。 ヘッダーのみをコピーしたため、QDCOUNTフィールドをリセットすることをお勧めします。リセットしないと、クライアント側でパーサーの警告が表示されます。 その後、リクエスト自体が削除されます。 この作業はすべてNF_INET_LOCAL_INフックで直接行うことができます。同じフックで、さらに統計を計算するためにソースIPをKFIFOキューに入れる必要があります。 着信パケットの統計を、赤黒ツリーを使用して、別のストリームで非同期的に検討します。 したがって、パケットの通過に最小限の遅延を導入します。KFIFOはロックのないデータ構造であり、さらに各CPUにキューが作成されます。 確かに、予想されるppsに応じて間隔を構成する必要があります。 CPUごとのデータに割り当てられるメモリのサイズにはまだ制限があります。現在は、CPUごとに4096個のIPアドレスのキューを考慮して32kBです。 したがって、100msの間隔を選択すると、各CPUに対して最大40960 ppsを計算できます。ほとんどの場合、これで十分と思われます。 一方、キューをオーバーフローさせると、統計用の一部のデータが失われます。
論理的な疑問が生じます。なぜハッシュを使用しないのですか?
残念ながら、そのような場所でハッシュを不正確に使用すると、別のタイプの攻撃の可能性が開かれます-衝突を引き起こすことを目的とした攻撃:実行時に重要なコードの一部でハッシュが使用されていることを知っているため、ハッシュで操作を行うようなデータを取得することができますテーブルは、O(1)ではなくO(n)を超えて既に発生します。 このような攻撃は、それらを特定するのが難しい可能性があるという点でも不快です。明らかに何も起こらず、サーバーが病気になりました。
ブロックされたIPからのppsがしきい値より小さい場合、ブロックは削除されます。 デフォルトでは、しきい値の10%に等しいヒステリシスを設定できます。
記事の最後にプロジェクトへのリンクがあります。 建設的なコメント、提案、追加を歓迎します。
使用例
組み立てられたモジュールがあるディレクトリで、実行します
insmod ./kfdns.ko threshold=100 period=100 hysteresis=10
threshold-tcフラグを設定するしきい値。
period-カウント期間、ms(つまり、この場合、1つのIPから100を超えるパケットが100ミリ秒で受信された場合、フィルターは機能します);
ヒステリシス-応答しきい値とフィルター解放しきい値の差。 ヒント:ヒステリシス=しきい値を設定すると、ロックがトリガーされた後、ロックが解放されることはありません。場合によっては便利です。
モジュールをロードした後
cat /proc/net/kfdns
IPのフィルタリングに該当する統計を見つけることができます。
試験結果
寄生負荷を作成するために、dnsperfが使用されました(2つのインスタンスで、1つは隣接する仮想マシンに、2つ目はラップトップにあり、残念ながらこれはシステムを障害にロードするのに十分ではありませんでした)、dnsサーバーはCentOSを実行するKVM仮想マシンで発生しましたdnsサーバー自体はpdns-recursorによって使用されました。
グラフには、モジュールのアクティブ化前、アクティブ化後、およびモジュールがアンロードされた状態のカウンター値が表示されます。 実験全体のPPSは80kppsのレベルでした。
したがって、私たちが達成したのは、発信トラフィックの削減です。 モジュールをオンにした後、発信トラフィックが着信トラフィックよりも少なくなったことがわかります。
原則として論理的です。タイトルのみをコピーすることを忘れないでください。
コンテキストスイッチの数を急激に減らすことは良いことです。
そして、システムで起こったことは次のとおりです。システム時間の消費の顕著な減少、ユーザー時間は目に見えます。 この場合のスチール時間の変更は仮想化の影響であり、これも論理的です。 しかし、irq時間のわずかな増加は興味深いものであり、さらなる実験の理由かもしれません。
今後何を追加しますか?
- PREROUTINGフックから(またはFORWARDINGからですが、確認する必要があります)作業します。 これにより、DNSサーバーだけでなく、たとえば、バランサーや境界ファイアウォールでもモジュールを使用できます。
- メインディストリビューション用のパッケージの準備。
- ドキュメント、ベストプラクティス
プロジェクト自体:
github.com/dcherednik/kfdns4linuxプロジェクトはかなり若い状態ですが、Khabrasocietyに関心のある人々がいて、おそらく誰かに役立つことを願っています。
参考文献と文献:
Linux netfilter Hacking HOWTONetfilterモジュールの作成信頼できないロックガイドISBN 978-5-8459-1779-9 Robert Love、The Linux Kernel:開発プロセスの説明