
PathAuditorは、libc関数を監査することでファイルアクセス関連の脆弱性を発見するためのツールです。
基本的なアイデアは以下の通りです:
LD_PRELOADを使用して、すべてのファイルシステム関連のライブラリコールをフックし、遭遇した違反をsyslogに記録しています。
これは公式にサポートされているGoogle製品ではありません。
このツールで検出できる脆弱性の例を見てみましょう。 CVE-2019-3461は、/tmpを走査して古いファイルを削除するツールtmpreaperのバグでした。通常はcronジョブとしてrootで実行されます。/tmp外のファイルを削除しないようにするため、ディレクトリがマウントポイントかどうかを確認するために以下のコードを使用していました。
if (S_ISDIR (sb.st_mode)) {
char *dst;
if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL)
message (LOG_FATAL, "malloc failed.\n");
strcpy(dst, ent->d_name);
strcat(dst, "/X");
rename(ent->d_name, dst);
if (errno == EXDEV) {
free(dst);
message (LOG_VERBOSE,
"File on different device skipped: `%s/%s'\n",
dirname, ent->d_name);
continue;
}
// [...]
要するに、このコードは rename("/tmp/foo", "/tmp/foo/x") を呼び出し、"/tmp/foo" がマウントポイントであれば EXDEV を返します。
PathAuditorは、"/tmp/foo" がroot以外のユーザーによって所有されている場合、この呼び出しを潜在的な脆弱性としてフラグ付けします。
その理由を理解するには、renameシステムコールがカーネルで実行されるときに何が起こるかを考える必要があります(簡略化):
"/tmp/foo" を辿ります。"/tmp/foo/x" を辿ります。ここでは "/tmp/foo" が2回解決されるため、競合状態が発生します。ユーザーが制御できる場合、任意の時点で別のファイルに置き換えることができます。
特に、まず "/tmp/foo" がディレクトリであることを確認し、tmpreaperコードの if(S_ISDIR) チェックを通過させます。その後、コードがシステムコールに入る直前にファイルに置き換えます。カーネルが最初の引数を解決するとき、ユーザーが制御する内容のファイルが見えます。
ここで再び置き換え、今度は同じファイルシステム上の任意のディレクトリへのシンボリックリンクに置き換えます。カーネルはパスを2回目に解決し、シンボリックリンクをたどり、制御されたファイルを選択したディレクトリに移動します。
同じファイルシステムという制限があるのは、renameがファイルシステム間で機能しないためです。しかし、一部のLinuxディストリビューションでは、デフォルトで/tmpはルートファイルシステム上の単なるフォルダであり、このバグを使ってファイルを/etc/cronに移動し、rootとして実行される可能性があります。
試すには、bazelでlibpath_auditor.soをビルドし、LD_PRELOADを使用してバイナリにロードする必要があります。違反はすべてsyslogに記録されるので、syslogが実行されていることを確認してください。
bazel build //pathauditor/libc:libpath_auditor.so
LD_PRELOAD=/path/to/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
tail /var/log/syslog
これを/etc/ld.so.preloadに追加することで、システム上のすべてのプロセスで実行することも可能です。ただし、これはテストシステムでのみ推奨され、不安定になる可能性があることに注意してください。
クイックスタートとして、このプロジェクトに含まれているDockerコンテナを試すことができます:
docker build -t pathauditor-example .
docker run -it pathauditor-example
# LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
# cat /var/log/syslog