Linuxカーネルのアクセス制御セキュリティを強化する方法

Linuxカーネルのアクセス制御セキュリティを強化する方法

背景

以前、当社のプロジェクト チームは、Windows、Linux、macOS の 3 つの主要なオペレーティング システム プラットフォームに関連するオペレーティング システム セキュリティの分野で、お客様のいくつかの問題の解決を支援していました。どのようなオペレーティングシステムであっても、本質的にはソフトウェアです。ソフトウェアは、最初に設計された時点では、人々のニーズを 100% 満たすことはできません。オペレーティングシステムでも同じです。人々のニーズを可能な限り満たすためには、人々がオペレーティングシステムをカスタマイズするための何らかのメカニズムを提供する必要があります。もちろん、公式のメカニズムに加えて、いくつかのブラックマジックもあります。これらのブラックマジックの使用は推奨されていませんが、特定のビジネスシナリオに直面したときに参考として使用できる場合があります。

Linux における一般的な傍受とフィルタリング

この記事では、Linux プラットフォームでよく見られるインターセプションに焦点を当てます。

  • ユーザー モードの動的ライブラリのインターセプション。
  • カーネルモードのシステムコールのインターセプト。
  • スタック可能なファイル システムのインターセプション。
  • インラインフックインターセプション。
  • LSM (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 に入り、特定のシステム コールを検索します。この配列内のアドレスを独自のプログラム アドレスに変更すると、システム コール ハイジャックを実現できます。ただし、セキュリティ上の理由から、カーネルはこの操作にいくつかの制限を設けています。

  • sys_ call _table のシンボルはエクスポートされず、直接取得することはできません。
  • sys_ call _table が配置されているメモリ ページは読み取り専用であり、直接変更することはできません。

上記の 2 つの問題に対する解決策は次のとおりです (複数の方法があります)。

  • sys call テーブルのアドレスを取得します: grep sys_call_table /boot/System.map-uname -r
  • ページ テーブルの読み取り専用属性は、CR0 レジスタの WP ビットによって制御されます。このビットをクリアすると、読み取り専用ページ テーブルを変更できます。
/* ページを書き込み可能にする */
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 セキュリティ フレームワークです。仕組みは次のとおりです:

エルエスエム

カーネルでは次の作業が行われます。

  • 特定のカーネル データ構造にセキュリティ ドメインを追加します。
  • カーネル ソース コードのさまざまな重要なポイントにセキュリティ フック関数の呼び出しを挿入します。
  • 共通のセキュリティ システム コールを追加します。
  • カーネル モジュールをセキュリティ モジュールとして登録したり、登録解除したりできるようにする関数が提供されます。
  • 機能ロジックのほとんどは、スケーラビリティのためにオプションのセキュリティ モジュールに移行されます。

適用可能なシナリオ

上記のフックメソッドにはさまざまな適用シナリオがあります。

  • ダイナミック ライブラリのハイジャックは完了していません。ハイジャックされた情報は、私たちのニーズを満たさない可能性があります。また、あなたより前に誰かがハイジャックした可能性もあります。LD_PRELOAD が無効になると、無効になります。
  • システムコールハイジャックでは、struct ファイル構造を取得できない、ファイルの絶対パスを取得できないなど、ハイジャックされた情報が私たちのニーズを満たさない可能性があります。
  • スタックされたファイル システムはマウントに依存しており、システムの再起動が必要になる場合があります。
  • インライン フックは柔軟性が高く、自由にフックできます。再起動せずにすぐに有効になります。ただし、異なるカーネル バージョン間の互換性が低く、一部の関数が変更されるとフックが無効になります。
  • LSM の場​​合、初期のカーネルでは、1 つの LSM カーネル モジュールのみをロードできました。たとえば、SELinux がロードされると、他の LSM モジュールをロードできませんでした。この問題は、最新のカーネル バージョンでは発生しません。

要約する

スペースの制限により、この記事では Linux 上の傍受技術についてのみ紹介します。Windows および macOS 上の傍受技術については、後ほど説明する機会があります。実際、同様の監査フックは、カーネルだけでなく、あらゆるシステムで厳格に要求されています。ますます多くの VM やランタイム、さらには多くの Web コンポーネントやフロントエンド アプリケーションが、より柔軟なフック メソッドを提供していることがわかります。これは、透明性とリアルタイム パフォーマンスという 2 つの主要なセキュリティ トレンドの下で最も一般的なソリューションです。

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • Linuxカーネルの一般的なリンクリスト学習のまとめ
  • Linux のカーネルリンクリストの例の詳細な説明
  • Linuxカーネルのリンクリスト実装プロセス
  • Linux カーネル デバイス ドライバー proc ファイル システム ノート
  • Linux カーネル デバイス ドライバー 高度な文字デバイス ドライバーのメモ
  • Linux カーネル デバイス ドライバー Linux カーネル モジュールの読み込みメカニズム メモの概要
  • Linux カーネル デバイス ドライバー アドレス マッピングに関する注意事項
  • Linux カーネル デバイス ドライバー Linux カーネル 基本メモの概要
  • Linuxカーネル空間とユーザー空間の実装と分析
  • Linuxカーネルプロセススケジューリング関数schedule()のトリガーと実行タイミングの詳細な説明
  • Linux カーネル デバイス ドライバー カーネル リンク リストの使用上の注意

<<:  InnoDB エンジンのパフォーマンスを最適化するための my.cnf パラメータ構成

>>:  WeChatミニプログラムがシームレスなスクロールを実現

推薦する

MySQL の最適化: サブクエリの代わりに結合を使用する

サブクエリの代わりにJOINを使用するMySQL はバージョン 4.1 以降で SQL サブクエリを...

Vue.jsはシンプルな折りたたみパネルを実装します

この記事では、Vue.jsの具体的なコードを共有して、シンプルな折りたたみパネルを実装する例を紹介し...

Alibaba Cloud Server Linux システムは Tomcat を構築して Web プロジェクトを展開します

私は全体のプロセスを 4 つのステップに分けます。 JDKをダウンロードしてインストールするTomc...

Linux は n 日前のログとサンプルコマンドを自動的に削除します

1. ファイル削除コマンド:対応するディレクトリを検索します -mtime + 日数 -name &...

Linux ファイルを分割するための split コマンドの詳細な説明

いくつかの簡単な Linux コマンドを使用すると、ストレージまたは電子メールの添付ファイルのサイズ...

Dockerイメージとコンテナの一般的な操作の詳細な説明

画像アクセラレータ中国の Docker Hub からイメージをプルすることが難しい場合があります。こ...

CSSはフロントエンドの画像変形の問題を完璧に解決します

Toutiao IT School で、CSS がフロントエンドの画像変形の問題を完璧に解決するとい...

Linux仮想マシンの静的IPアドレスを構成するための手順を完了します

序文多くの場合、仮想マシンを使用します。たとえば、一部のテストは検出されません。何かを壊すことを心配...

HTMLページでチェックボックスを操作する方法

チェックボックスは Web ページで非常によく使用されます。e コマースの Web サイトでもプラッ...

Centos7 ベースの Nginx Web サイト サーバーの構築の詳細説明 (仮想 Web ホストの構成を含む)

1. Nginx サービス基盤Nginx (エンジン x) は、パフォーマンスの最適化のために特別...

JS配列の一般的な方法とテクニックを学び、マスターになりましょう

目次splice() メソッドjoin() メソッド逆() メソッドevery() メソッド削減()...

Docker が elasticsearch を起動するときのメモリ不足の問題と解決策

質問Docker が elasticsearch をインストールして起動するときにメモリが不足するシ...

JavaScriptはシンプルな計算機能を実装します

この記事では、参考までに、簡単な計算機を実装するためのJavaScriptの具体的なコードを紹介しま...

Jenkins初心者のためのDockerデプロイメントチュートリアルの詳細な説明

この記事では、docker 経由で Jenkins+Maven+SVN+Tomcat をデプロイし、...

TSオブジェクトのスプレッド演算子とレスト演算子の詳細な説明

目次概要オブジェクトの残り属性オブジェクトの拡張プロパティオブジェクトの浅いコピーを作成するkeyo...