背景 以前、当社のプロジェクト チームは、Windows、Linux、macOS の 3 つの主要なオペレーティング システム プラットフォームに関連するオペレーティング システム セキュリティの分野で、お客様のいくつかの問題の解決を支援していました。どのようなオペレーティングシステムであっても、本質的にはソフトウェアです。ソフトウェアは、最初に設計された時点では、人々のニーズを 100% 満たすことはできません。オペレーティングシステムでも同じです。人々のニーズを可能な限り満たすためには、人々がオペレーティングシステムをカスタマイズするための何らかのメカニズムを提供する必要があります。もちろん、公式のメカニズムに加えて、いくつかのブラックマジックもあります。これらのブラックマジックの使用は推奨されていませんが、特定のビジネスシナリオに直面したときに参考として使用できる場合があります。 Linux における一般的な傍受とフィルタリング この記事では、Linux プラットフォームでよく見られるインターセプションに焦点を当てます。
動的ライブラリのハイジャック Linux での動的ライブラリのハイジャックは、主に LD_PRELOAD 環境変数に基づいています。この環境変数の主な機能は、動的ライブラリの読み込み順序を変更し、ユーザーが異なる動的ライブラリ内の同じ関数を選択的に読み込むことができるようにすることです。ただし、不適切な使用は深刻なセキュリティ問題を引き起こします。メイン プログラムやダイナミック リンク ライブラリに他の動的関数をロードするために使用することができ、これにより、他の人のプログラムに悪意のあるコードを挿入する機会が生まれます。 次のようなユーザー名とパスワードの検証機能があるとします。 #include <stdio.h> #include <文字列.h> #include <stdlib.h> int main(int argc, char **argv) { char passwd[] = "パスワード"; (引数<2)の場合{ printf("無効なargcです!\n"); 戻る; } (!strcmp(passwd、argv[1]))の場合{ printf("パスワードが正しいです!\n"); 戻る; } printf("無効なパスワードです!\n"); } 比較が常に正しいことを確認するために、別の hookStrcmp プログラムを作成しましょう。 #include <stdio.h> int strcmp(const char *s1, const char *s2) { /* 常に 0 を返し、2 つの文字列が等しいことを示します */ 0を返します。 } 次のコマンドを順番に実行すると、フック プログラムが最初に実行されます。 gcc -Wall -fPIC -shared -o hookStrcmp.so hookStrcmp.c LD_PRELOAD="./hookStrcmp.so" をエクスポートします その結果、自分で書いた strcmp 関数が最初に呼び出されることがわかります。これは最も単純なハイジャックですが、geteuid/getuid/getgid のようなものをハイジャックして 0 を返すようにすると、ルート権限を公開するのと同じことになります。したがって、安全上の理由から、LD_PRELOAD 環境変数は通常無効になっています。 Linux システムコールのハイジャック 最近、4.4.0 カーネルには 513 を超えるシステム コール (その多くは一度も使用されたことがない) があることが発見されました。システム コール ハイジャックの目的は、システム内の元のシステム コールを変更し、元のシステム コールを独自のプログラムに置き換えることです。 Linux カーネル内のすべてのシステム コールは、sys_call_table と呼ばれるカーネル配列に配置され、配列の値はシステム コール サービス プログラムのエントリ アドレスを表します。システムコールのプロセス全体は次のとおりです。 ユーザー モードでシステム コールが開始されると、80 ソフト割り込みを介して syscall ハンドラーに入り、次にグローバル システム コール テーブル sys_ call _table に入り、特定のシステム コールを検索します。この配列内のアドレスを独自のプログラム アドレスに変更すると、システム コール ハイジャックを実現できます。ただし、セキュリティ上の理由から、カーネルはこの操作にいくつかの制限を設けています。
上記の 2 つの問題に対する解決策は次のとおりです (複数の方法があります)。
/* ページを書き込み可能にする */ int make_rw(符号なしロングアドレス) { 符号なし整数レベル; pte_t *pte = lookup_address(address, &level);//仮想アドレスが配置されているページ テーブル アドレスを検索します pte->pte |= _PAGE_RW;//ページ テーブルの読み取りおよび書き込み属性を設定します return 0; } /* ページを書き込み禁止にする */ int make_ro(符号なしロングアドレス) { 符号なし整数レベル; pte_t *pte = lookup_address(アドレス、レベル); pte->pte &= ~_PAGE_RW; //読み取り専用属性を設定する return 0; } システムコールの置き換えを開始する この記事では、ls コマンドに対応するシステム コールを実装します。システム コール番号は _NR _getdents です。 静的 int syscall_init_module(void) { orig_getdents = sys_call_table[__NR_getdents]; make_rw((unsigned long)sys_call_table); //ページ属性を変更するsys_call_table[__NR_getdents] = (unsigned long *)hacked_getdents; //新しいシステムコールアドレスを設定するmake_ro((unsigned long)sys_call_table); 0を返します。 } 復元 静的 void syscall_cleanup_module(void) { printk(KERN_ALERT "モジュールのシステムコールがアンロードされました。\n"); make_rw((符号なしlong)sys_call_table); sys_call_table[__NR_getdents] = (符号なしlong *)orig_getdents; make_ro((符号なしlong)sys_call_table); } Makefile を使用してコンパイルし、insmod を使用してカーネル モジュールを挿入してから ls を実行すると、システム コールが開始されます。フック コード内の特定のファイルを削除できます。ls ではこれらのファイルが表示されませんが、これらのファイルは引き続き存在します。 スタックファイルシステム Linux は vfs 仮想ファイルシステムを使用して特定のディスクファイルシステムを統合および抽象化し、上から下までスタックのような IO スタックを形成します。カーネル ソース コードを分析すると、読み取り操作を例にとると、上から下に実行されるプロセスは次のようになります。 カーネルは、C 言語の形式、つまり関数ポインタの形式で、多くのオブジェクト指向プログラミングを使用します。たとえば、read は vfs によってユーザーに提供されるインターフェイスであり、その下にある特定の呼び出しは ext2 の読み取り操作です。 VFS が提供するさまざまなインターフェースを実装すれば、スタックファイルシステムを実現できます。いくつかのスタックされたファイルシステムは Linux カーネルに統合されています。たとえば、Ubuntu をインストールするときに、ホームディレクトリを暗号化するように求められます。これは実際にはスタックされた暗号化ファイルシステム (eCryptfs) です。原理は次のとおりです。 スタック ファイル システムが実装されているため、すべての読み取りおよび書き込み操作がファイル システムに入るため、すべてのデータを取得して、インターセプションとフィルタリングを行うことができます。 以下は私が実装した最も単純なスタック ファイル システムです。これは、ファイルを開いて読み書きする最も簡単な方法を実装しています。小さいですが完全です。 https://github.com/wangzhangjun/wzjfs インラインフック カーネル内の関数がこの関数内ですべての関数を実装することは不可能であり、下位レベルの関数を呼び出す必要があることはわかっています。この下位レベル関数が、必要なフィルタリングされた情報コンテンツを取得できる場合は、上位レベル関数内の下位レベル関数のオフセットを新しい関数のオフセットに置き換えることができます。このようにして、上位レベル関数が下位レベル関数を呼び出すと、新しい関数にジャンプし、新しい関数でフィルタリングとコンテンツのハイジャックを実行します。したがって、原則として、インライン フックは任意の場所にフックできます。 インライン フックには 2 つの重要な問題があります。
最初の質問について: カーネルのソース コードに関する経験が必要です。たとえば、読み取り操作の場合、ソース コードは次のようになります。 ここで、read システム コールが開始されると、sys read に入り、sys read 内で vfs read 関数が呼び出されます。vfs read のパラメーターにはフィルタリングに必要な情報が含まれているため、vfs_read をフック ポイントとして使用できます。 2番目の質問について: フックする方法は?ここに 2 つの方法があります: 最初の方法は、バイナリ置換を直接実行し、呼び出し命令のオペランドをフック関数のアドレスに置き換えることです。 2 番目の方法: Linux カーネルによって提供される kprobes メカニズム。 原理としては、フックポイントに int 3 (x86) のマシンコードを挿入し、CPU がここで実行すると sig trap シグナルがトリガーされ、次にユーザー定義のフック関数を sig trap のコールバック関数に挿入して、フック関数をトリガーするという目的を達成します。これが実はデバッガーの原理です。 エルエスエム LSM は Linux Secrity Module の略で、Linux セキュリティ モジュールを意味します。これは、高効率、シンプルさ、使いやすさを特徴とする一般的な Linux セキュリティ フレームワークです。仕組みは次のとおりです:
カーネルでは次の作業が行われます。
適用可能なシナリオ 上記のフックメソッドにはさまざまな適用シナリオがあります。
要約する スペースの制限により、この記事では Linux 上の傍受技術についてのみ紹介します。Windows および macOS 上の傍受技術については、後ほど説明する機会があります。実際、同様の監査フックは、カーネルだけでなく、あらゆるシステムで厳格に要求されています。ますます多くの VM やランタイム、さらには多くの Web コンポーネントやフロントエンド アプリケーションが、より柔軟なフック メソッドを提供していることがわかります。これは、透明性とリアルタイム パフォーマンスという 2 つの主要なセキュリティ トレンドの下で最も一般的なソリューションです。 上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。 以下もご興味があるかもしれません:
|
<<: InnoDB エンジンのパフォーマンスを最適化するための my.cnf パラメータ構成
>>: WeChatミニプログラムがシームレスなスクロールを実現
サブクエリの代わりにJOINを使用するMySQL はバージョン 4.1 以降で SQL サブクエリを...
この記事では、Vue.jsの具体的なコードを共有して、シンプルな折りたたみパネルを実装する例を紹介し...
私は全体のプロセスを 4 つのステップに分けます。 JDKをダウンロードしてインストールするTomc...
1. ファイル削除コマンド:対応するディレクトリを検索します -mtime + 日数 -name &...
いくつかの簡単な Linux コマンドを使用すると、ストレージまたは電子メールの添付ファイルのサイズ...
画像アクセラレータ中国の Docker Hub からイメージをプルすることが難しい場合があります。こ...
Toutiao IT School で、CSS がフロントエンドの画像変形の問題を完璧に解決するとい...
序文多くの場合、仮想マシンを使用します。たとえば、一部のテストは検出されません。何かを壊すことを心配...
チェックボックスは Web ページで非常によく使用されます。e コマースの Web サイトでもプラッ...
1. Nginx サービス基盤Nginx (エンジン x) は、パフォーマンスの最適化のために特別...
目次splice() メソッドjoin() メソッド逆() メソッドevery() メソッド削減()...
質問Docker が elasticsearch をインストールして起動するときにメモリが不足するシ...
この記事では、参考までに、簡単な計算機を実装するためのJavaScriptの具体的なコードを紹介しま...
この記事では、docker 経由で Jenkins+Maven+SVN+Tomcat をデプロイし、...
目次概要オブジェクトの残り属性オブジェクトの拡張プロパティオブジェクトの浅いコピーを作成するkeyo...