最初に、私が何をしたいのか、次に何とかして何ができるのか、最後に私の問題について説明しましょう。
目標:Cでのフラッシュ+フラッシュキャッシュ攻撃の実装
私はCでフラッシュ+フラッシュキャッシュ攻撃を実装しようとしています( https://gruss.cc/files/flushflush.pdf )。基本的に、共有ライブラリを使用する場合、2つの異なるプロセスが同じメモリページを共有する可能性があるという事実を利用します。これにより、キャッシュの「共有使用」が発生します。
共有ライブラリからインポートされた関数func
を継続的に実行し、場合によっては実行する犠牲プロセスがあると仮定します。
並行して、被害者と同じコンピューター上で実行されているスパイプロセスがあり、被害者がfunc
を呼び出したときにスパイすることを目的としていると想定します。スパイも同じ共有ライブラリにアクセスできます。スパイプロセスの擬似コードは次のとおりです。
i=0;
for (i = 0; i < trace_length ; i++)
{
trace[i] = flush_time( address of function "func");
i++;
}
ここで、flush_time(
<address>
)
は、CPUがaddress
すべてのキャッシュレベルから。 Intelプロセッサでは、これはアセンブリ命令clflush
によって実現できます。アドレスがキャッシュに存在しない場合、clflush
の実行が高速になることがわかります。その結果、メモリアドレスをフラッシュするために必要なタイミングは、キャッシュ内に存在する(または存在しない)場合に直接変換できます。
Spyプロセスは、時間の経過に伴うflush_timeの結果を含むトレースベクトルを返します。以前の観察から、被害者も関数func
を呼び出した場合、このトレースはより高いタイミングを示します。したがって、スパイは被害者がfunc
を呼び出しているときに差し引きます。
私が何とかすること:GSL共有ライブラリに対して攻撃を機能させる
前述の攻撃を実装しました。共有ライブラリは [〜#〜] gsl [〜#〜] です。恣意的に、私はgsl_stats_mean
(gsl_statistics_double
で定義)を関数func
として機能するように選択しました。
その場合、被害者のプログラムがgsl_stats_mean
を呼び出したときのタイミングの違いがはっきりとわかるので、スパイは完全に機能します。
私の問題:攻撃は自家製の共有ライブラリでは機能しません
自分の共有ライブラリを作成し、それをスパイ/犠牲者テストに使用したいと思います。 .
が私のspy.c
ファイルとvictim.c
ファイルがあるフォルダーを示していると仮定します。フォルダーmyl.c
に2つのファイルmyl.h
と./src/myl
を作成しました。これらのファイルには、それぞれfunc
の説明とその宣言が含まれています。以前のように、私のスパイの目的は、被害者からのfunc
の使用を検出することです。
spy.c
とvictim.c
の両方にインクルード行が含まれています。
#include "src/myl/myl.h"
共有ライブラリの作成は、次のコマンドを使用して実行されます。
gcc -c -fPIC src/myl/myl.c -o bin/shared/myl.o #creation of object in ./bin/shared
gcc -shared bin/shared/myl.o -o bin/shared/libmyl.so #creation of the shared library in ./bin/shared
gcc -c spy.c -o spy.o #creation of spy's process object file
gcc -c victim.c -o victim.o #creation of victim's process object file
gcc spy.o -Lbin/shared -lmyl -o spy #creation of spy's executable
gcc victim.o -Lbin/shared -lmyl -o victim #creation of victim's executable
次に、次の行を使用して犠牲者とスパイを起動します。
LD_LIBRARY_PATH=$(pwd)/bin/shared ./victim
LD_LIBRARY_PATH=$(pwd)/bin/shared ./spy
ただし、GSL関数を使用していた場合とは対照的に、キャッシュ上のアクティビティは表示されなくなりました。これは、私のスパイと被害者のプロセスが共有ライブラリの同じメモリページを共有していないことを意味していると思います(ただし、GSLを使用している場合はそうでした)。この方法でコンパイルする場合、GSL関数をターゲットにするときにスパイは引き続き機能することに注意してください。
私の主な質問は次のとおりです:自家製のコンパイル済み共有ライブラリが、同時に複数のプロセスによって実行されているときに、メモリページングが共有されるようにする方法は? GSL、gmp、ネイティブライブラリなど、私がインストールした「適切な」ライブラリはそうです...しかし、私が自分で作成したものはそうではありません。
よろしくお願いいたします。答えが簡単なものである場合は、お詫び申し上げます。
編集:スパイと被害者の両方のLD_DEBUG=libs
とfiles
の出力。注:被害者はpg2
と呼ばれ、スパイはpg1
と呼ばれます(申し訳ありません)
最初に被害者用のライブラリ、次に被害者用のファイル(pg2
)。次に、スパイ用のlibs、続いてスパイ用のファイル(pg1
):
LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
31714: find library=libmyl.so [0]; searching
31714: search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31714: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
31714: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
31714: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
31714: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31714:
31714: find library=libc.so.6 [0]; searching
31714: search path=/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31714: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
31714: search cache=/etc/ld.so.cache
31714: trying file=/lib/x86_64-linux-gnu/libc.so.6
31714:
31714:
31714: calling init: /lib/x86_64-linux-gnu/libc.so.6
31714:
31714:
31714: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31714:
31714:
31714: initialize program: ./pg2
31714:
31714:
31714: transferring control: ./pg2
31714:
LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg2
31901:
31901: file=libmyl.so [0]; needed by ./pg2 [0]
31901: file=libmyl.so [0]; generating link map
31901: dynamic: 0x00007f5a3b34be48 base: 0x00007f5a3b14b000 size: 0x0000000000201028
31901: entry: 0x00007f5a3b14b580 phdr: 0x00007f5a3b14b040 phnum: 7
31901:
31901:
31901: file=libc.so.6 [0]; needed by ./pg2 [0]
31901: file=libc.so.6 [0]; generating link map
31901: dynamic: 0x00007f5a3b144ba0 base: 0x00007f5a3ad81000 size: 0x00000000003c99a0
31901: entry: 0x00007f5a3ada1950 phdr: 0x00007f5a3ad81040 phnum: 10
31901:
31901:
31901: calling init: /lib/x86_64-linux-gnu/libc.so.6
31901:
31901:
31901: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31901:
31901:
31901: initialize program: ./pg2
31901:
31901:
31901: transferring control: ./pg2
31901:
LD_DEBUG=libs LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
31938: find library=libmyl.so [0]; searching
31938: search path=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared/tls:/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64:/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/x86_64/libmyl.so
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/tls/libmyl.so
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/x86_64/libmyl.so
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31938:
31938: find library=libgsl.so.23 [0]; searching
31938: search path=/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgsl.so.23
31938: search cache=/etc/ld.so.cache
31938: trying file=/usr/local/lib/libgsl.so.23
31938:
31938: find library=libgslcblas.so.0 [0]; searching
31938: search path=/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libgslcblas.so.0
31938: search cache=/etc/ld.so.cache
31938: trying file=/usr/local/lib/libgslcblas.so.0
31938:
31938: find library=libc.so.6 [0]; searching
31938: search path=/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libc.so.6
31938: search cache=/etc/ld.so.cache
31938: trying file=/lib/x86_64-linux-gnu/libc.so.6
31938:
31938: find library=libm.so.6 [0]; searching
31938: search path=/home/romain/Documents/work/test/page_sharing/bin/shared (LD_LIBRARY_PATH)
31938: trying file=/home/romain/Documents/work/test/page_sharing/bin/shared/libm.so.6
31938: search cache=/etc/ld.so.cache
31938: trying file=/lib/x86_64-linux-gnu/libm.so.6
31938:
31938:
31938: calling init: /lib/x86_64-linux-gnu/libc.so.6
31938:
31938:
31938: calling init: /lib/x86_64-linux-gnu/libm.so.6
31938:
31938:
31938: calling init: /usr/local/lib/libgslcblas.so.0
31938:
31938:
31938: calling init: /usr/local/lib/libgsl.so.23
31938:
31938:
31938: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31938:
31938:
31938: initialize program: ./pg1
31938:
31938:
31938: transferring control: ./pg1
31938:
0: 322 # just some output of my spying program
1: 323 # just some output of my spying program
31938:
31938: calling fini: ./pg1 [0]
31938:
31938:
31938: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
31938:
31938:
31938: calling fini: /usr/local/lib/libgsl.so.23 [0]
31938:
31938:
31938: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
31938:
31938:
31938: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
31938:
LD_DEBUG=files LD_LIBRARY_PATH=$(pwd)/bin/shared ./pg1
31940:
31940: file=libmyl.so [0]; needed by ./pg1 [0]
31940: file=libmyl.so [0]; generating link map
31940: dynamic: 0x00007fb3d8794e48 base: 0x00007fb3d8594000 size: 0x0000000000201028
31940: entry: 0x00007fb3d8594580 phdr: 0x00007fb3d8594040 phnum: 7
31940:
31940:
31940: file=libgsl.so.23 [0]; needed by ./pg1 [0]
31940: file=libgsl.so.23 [0]; generating link map
31940: dynamic: 0x00007fb3d8582ac8 base: 0x00007fb3d8126000 size: 0x000000000046da60
31940: entry: 0x00007fb3d8180e30 phdr: 0x00007fb3d8126040 phnum: 7
31940:
31940:
31940: file=libgslcblas.so.0 [0]; needed by ./pg1 [0]
31940: file=libgslcblas.so.0 [0]; generating link map
31940: dynamic: 0x00007fb3d8124df0 base: 0x00007fb3d7ee8000 size: 0x000000000023d050
31940: entry: 0x00007fb3d7eea120 phdr: 0x00007fb3d7ee8040 phnum: 7
31940:
31940:
31940: file=libc.so.6 [0]; needed by ./pg1 [0]
31940: file=libc.so.6 [0]; generating link map
31940: dynamic: 0x00007fb3d7ee1ba0 base: 0x00007fb3d7b1e000 size: 0x00000000003c99a0
31940: entry: 0x00007fb3d7b3e950 phdr: 0x00007fb3d7b1e040 phnum: 10
31940:
31940:
31940: file=libm.so.6 [0]; needed by /usr/local/lib/libgsl.so.23 [0]
31940: file=libm.so.6 [0]; generating link map
31940: dynamic: 0x00007fb3d7b1cd88 base: 0x00007fb3d7815000 size: 0x00000000003080f8
31940: entry: 0x00007fb3d781a600 phdr: 0x00007fb3d7815040 phnum: 7
31940:
31940:
31940: calling init: /lib/x86_64-linux-gnu/libc.so.6
31940:
31940:
31940: calling init: /lib/x86_64-linux-gnu/libm.so.6
31940:
31940:
31940: calling init: /usr/local/lib/libgslcblas.so.0
31940:
31940:
31940: calling init: /usr/local/lib/libgsl.so.23
31940:
31940:
31940: calling init: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so
31940:
31940:
31940: initialize program: ./pg1
31940:
31940:
31940: transferring control: ./pg1
31940:
0: 325 # just some output of my spying program
1: 327 # just some output of my spying program
31940:
31940: calling fini: ./pg1 [0]
31940:
31940:
31940: calling fini: /home/romain/Documents/work/test/page_sharing/bin/shared/libmyl.so [0]
31940:
31940:
31940: calling fini: /usr/local/lib/libgsl.so.23 [0]
31940:
31940:
31940: calling fini: /usr/local/lib/libgslcblas.so.0 [0]
31940:
31940:
31940: calling fini: /lib/x86_64-linux-gnu/libm.so.6 [0]
31940:
ld
動的リンカー/ローダーからのデバッグ出力は、victim
とspy
の両方のプログラムが正しい入力ファイルをロードすることを確認するため、次のステップはカーネル実際にlibmyl.so
がvictim
とspy
の間で共有されるようにメモリに読み込まれる物理ページを設定しました。
Linuxでは、カーネルの2.6.25以降、カーネルの pagemap
インターフェースを介してこれを確認できます。これにより、ユーザー空間プログラムは、/proc
のファイルを読み取ることでページテーブルと関連情報を調べることができます。
2つのプロセスがメモリを共有しているかどうかを確認するためにページマップを使用する一般的な手順は、次のようになります。
/proc/<pid>/maps
を使用して、メモリ空間のどの部分がどのオブジェクトにマップされているかを判別します。libmyl.so
がマップされているページです。/proc/<pid>/pagemap
を開きます。 pagemap
は、ページごとに1つずつ、64ビットのページマップ記述子で構成されています。 pagemap
内のページのアドレスとその記述子アドレスの間のマッピングは、ページアドレス/ページサイズ*記述子サイズです。調べたいページの記述子を探します。pagemap
から各ページの64ビット記述子を符号なし整数として読み取ります。victim
とspy
のlibmyl.so
ページ間で、ページ記述子のビット0〜54のページフレーム番号(PFN)を比較します。 PFNが一致する場合、2つのプロセスは同じ物理ページを共有しています。次のサンプルコードは、プロセス内からpagemap
にアクセスして出力する方法を示しています。 dl_iterate_phdr()
を使用して、プロセスのメモリ空間にロードされた各共有ライブラリの仮想アドレスを決定し、対応するpagemap
を検索して/proc/<pid>/pagemap
から出力します。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <link.h>
#include <errno.h>
#include <error.h>
#define E_CANNOT_OPEN_PAGEMAP 1
#define E_CANNOT_READ_PAGEMAP 2
typedef struct __attribute__ ((__packed__)) {
union {
uint64_t pmd;
uint64_t page_frame_number : 55;
struct {
uint64_t swap_type: 5;
uint64_t swap_offset: 50;
uint64_t soft_dirty: 1;
uint64_t exclusive: 1;
uint64_t zero: 4;
uint64_t file_page: 1;
uint64_t swapped: 1;
uint64_t present: 1;
};
};
} pmd_t;
static int print_pagemap_for_phdr(struct dl_phdr_info *info,
size_t size, void *data)
{
struct stat statbuf;
size_t pagesize = sysconf(_SC_PAGESIZE);
char pagemap_path[BUFSIZ];
int pagemap;
uint64_t start_addr, end_addr;
if (!strcmp(info->dlpi_name, "")) {
return 0;
}
stat(info->dlpi_name, &statbuf);
start_addr = info->dlpi_addr;
end_addr = (info->dlpi_addr + statbuf.st_size + pagesize) & ~(pagesize-1);
printf("\n%10p-%10p %s\n\n",
(void *)start_addr,
(void *)end_addr,
info->dlpi_name);
snprintf(pagemap_path, sizeof pagemap_path, "/proc/%d/pagemap", getpid());
if ((pagemap = open(pagemap_path, O_RDONLY)) < 0) {
error(E_CANNOT_OPEN_PAGEMAP, errno,
"cannot open pagemap: %s", pagemap_path);
}
printf("%10s %8s %7s %5s %8s %7s %7s\n",
"", "", "soft-", "", "file /", "", "");
printf("%10s %8s %7s %5s %11s %7s %7s\n",
"address", "pfn", "dirty", "excl.",
"shared anon", "swapped", "present");
for (unsigned long i = start_addr; i < end_addr; i += pagesize) {
pmd_t pmd;
if (pread(pagemap, &pmd.pmd, sizeof pmd.pmd, (i / pagesize) * sizeof pmd) != sizeof pmd) {
error(E_CANNOT_READ_PAGEMAP, errno,
"cannot read pagemap: %s", pagemap_path);
}
if (pmd.pmd != 0) {
printf("0x%10" PRIx64 " %06" PRIx64 " %3d %5d %8d %9d %7d\n", i,
(unsigned long)pmd.page_frame_number,
pmd.soft_dirty,
pmd.exclusive,
pmd.file_page,
pmd.swapped,
pmd.present);
}
}
close(pagemap);
return 0;
}
int main()
{
dl_iterate_phdr(print_pagemap_for_phdr, NULL);
exit(EXIT_SUCCESS);
}
プログラムの出力は次のようになります。
$ Sudo ./a.out
0x7f935408d000-0x7f9354256000 /lib/x86_64-linux-gnu/libc.so.6
soft- file /
address pfn dirty excl. shared anon swapped present
0x7f935408d000 424416 1 0 1 0 1
0x7f935408e000 424417 1 0 1 0 1
0x7f935408f000 422878 1 0 1 0 1
0x7f9354090000 422879 1 0 1 0 1
0x7f9354091000 43e879 1 0 1 0 1
0x7f9354092000 43e87a 1 0 1 0 1
0x7f9354093000 424790 1 0 1 0 1
...
どこ:
address
はページの仮想アドレスですpfn
は、ページのページフレーム番号です。soft-dirty
は、ページのページテーブルエントリ(PTE)で soft-dirty bit が設定されているかどうかを示します。excl.
は、ページが排他的にマップされているかどうかを示します(つまり、ページはこのプロセスに対してのみマップされます)。file / shared anon
は、ページがファイルページであるか共有匿名ページであるかを示します。swapped
は、ページが現在スワップされているかどうかを示します(present
がゼロであることを意味します)。present
は、ページがプロセス常駐セットに現在存在するかどうかを示します(swapped
がゼロであることを意味します)。(注:Linux 4.0以降はCAP_SYS_ADMIN
機能を持つユーザーのみが/proc/<pid>/pagemap
からPFNを取得できるため、サンプルプログラムをSudo
で実行します。Linux4.2以降、ユーザーにCAP_SYS_ADMIN
がない場合、PFNフィールドはゼロになります。理由この変更は、PFNによって公開された仮想から物理へのマッピングに関する情報を使用して、別のメモリ関連の脆弱性である Rowhammer attack を利用することをより困難にするためです。)
サンプルプログラムを数回実行すると、ページの仮想アドレスが( [〜#〜] aslr [〜#〜] によって)変更されるはずですが、共有ライブラリのPFN他のプロセスで使用されているものは同じままである必要があります。
libmyl.so
のPFNがvictim
とspy
プログラム間で一致する場合、攻撃コード自体で攻撃が失敗する理由を探し始めます。 PFNが一致しない場合、追加のビットにより、ページが共有されるように設定されていない理由がわかります。 pagemap
ビット 以下を示します :
present file exclusive state:
0 0 0 non-present
1 1 0 file page mapped somewhere else
1 1 1 file page mapped only here
1 0 0 anonymous non-copy-on-write page (shared with parent/child)
1 0 1 anonymous copy-on-write page (or never forked)
(MAP_FILE | MAP_PRIVATE)
領域のコピーオンライトページは、このコンテキストでは匿名です。
ボーナス:ページがマップされた回数を取得するには、PFNを使用して/proc/kpagecount
でページを検索します。このファイルには、各ページがマップされ、PFNによってインデックスが付けられた回数の64ビットのカウントが含まれています。