Izinkan saya menjelaskan terlebih dahulu apa yang ingin saya lakukan, diikuti dengan apa yang berhasil saya lakukan, dan terakhir masalah saya.
Tujuan:menerapkan serangan cache flush+flush di C
Saya mencoba menerapkan dalam C serangan cache flush+flush (https://gruss.cc/files/flushflush.pdf). Pada dasarnya, ini mengeksploitasi fakta bahwa dua proses berbeda mungkin berbagi halaman memori yang sama saat menggunakan pustaka bersama. Ini menghasilkan "penggunaan bersama" dari cache.
Mari kita asumsikan bahwa kita memiliki proses korban, terus berjalan dan terkadang menjalankan fungsi func
diimpor dari perpustakaan bersama.
Secara paralel, kami berasumsi bahwa kami memiliki proses mata-mata, berjalan di komputer yang sama dengan korban, yang tujuannya adalah untuk memata-matai ketika korban memanggil func
. Mata-mata juga memiliki akses ke perpustakaan bersama yang sama. Pseudo-code proses mata-mata adalah sebagai berikut:
i=0;
for (i = 0; i < trace_length ; i++)
{
trace[i] = flush_time( address of function "func");
i++;
}
di mana flush_time(
<address>
)
adalah fungsi yang mengembalikan waktu yang diperlukan CPU untuk mengosongkan memori yang ditunjukkan oleh address
dari semua level cache. Pada prosesor Intel, ini dapat dicapai melalui instruksi perakitan clflush
. Seseorang dapat mengamati bahwa eksekusi clflush
lebih cepat ketika alamat tidak ada dalam cache. Akibatnya, waktu yang diperlukan untuk menghapus alamat memori dapat langsung diterjemahkan dengan kehadirannya (atau tidak) di dalam cache.
Proses mata-mata mengembalikan vektor jejak yang berisi hasil flush_time dari waktu ke waktu. Dari pengamatan sebelumnya, jejak ini akan menunjukkan waktu yang lebih tinggi ketika korban juga memanggil fungsi func
. Mata-mata dengan demikian akan memotong ketika korban memanggil func
.
Apa yang berhasil saya lakukan:membuat serangan berhasil terhadap pustaka bersama GSL
Saya menerapkan serangan yang disebutkan di atas, di mana perpustakaan bersama adalah GSL. Secara sewenang-wenang, saya memilih gsl_stats_mean
(didefinisikan dalam gsl_statistics_double
) untuk bertindak sebagai fungsi func
Saya bersedia untuk memata-matai.
Dalam hal ini, mata-mata bekerja dengan sempurna karena saya dapat dengan jelas melihat perbedaan waktu ketika program korban membuat panggilan ke gsl_stats_mean
Masalah saya:serangan tidak berhasil pada perpustakaan bersama buatan sendiri
Saya sekarang ingin membuat perpustakaan bersama saya sendiri dan menggunakannya untuk tes mata-mata/korban. Mari kita asumsikan .
menunjukkan folder tempat spy.c
saya dan victim.c
file adalah. Saya membuat dua file myl.c
dan myl.h
dalam folder ./src/myl
, yang masing-masing berisi deskripsi func
dan itu adalah deklarasi. Seperti sebelumnya, tujuan mata-mata saya adalah untuk mendeteksi penggunaan func
dari korban.
Keduanya spy.c
dan victim.c
berisi baris include:
#include "src/myl/myl.h"
Pembuatan perpustakaan bersama dilakukan dengan menggunakan perintah berikut:
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
Saya kemudian meluncurkan korban dan mata-mata saya menggunakan baris berikut:
LD_LIBRARY_PATH=$(pwd)/bin/shared ./victim
LD_LIBRARY_PATH=$(pwd)/bin/shared ./spy
Namun, berbeda dengan kasus di mana saya menggunakan fungsi GSL, saya tidak dapat melihat aktivitas apa pun di cache lagi. Saya kira ini berarti bahwa proses mata-mata dan korban saya tidak berbagi halaman memori yang sama untuk perpustakaan bersama saya (namun, itu terjadi ketika menggunakan GSL). Perhatikan bahwa saat mengompilasi dengan cara ini, mata-mata masih berfungsi saat menargetkan fungsi GSL.
Pertanyaan utama saya adalah sebagai berikut:bagaimana memastikan bahwa perpustakaan bersama yang dikompilasi buatan sendiri akan memiliki paging memori yang dibagikan ketika dijalankan oleh beberapa proses pada saat yang bersamaan? Tampaknya demikian halnya dengan pustaka "tepat" yang saya instal, seperti GSL, gmp, pustaka asli, dll…. Tapi tidak untuk yang saya buat sendiri.
Terima kasih sebelumnya, dan saya minta maaf jika jawabannya langsung.
EDIT:keluaran LD_DEBUG=libs
dan files
untuk mata-mata dan korban.
CATATAN:korban disebut pg2
dan mata-mata disebut pg1
(maaf soal itu)
Pertama, libs untuk korban, diikuti oleh file untuk korban (pg2
). Kemudian, lib untuk mata-mata, diikuti oleh file untuk mata-mata (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:
Jawaban yang Diterima:
Karena keluaran debug dari ld
penaut/pemuat dinamis mengonfirmasi bahwa kedua victim
dan spy
program memuat file input yang benar, langkah selanjutnya adalah memverifikasi apakah kernel benar-benar telah menyiapkan halaman fisik di mana libmyl.so
dimuat dalam memori untuk dibagikan di antara victim
dan spy
.
Di Linux ini dimungkinkan untuk memverifikasi sejak kernel versi 2.6.25 melalui pagemap
antarmuka di kernel yang memungkinkan program ruang pengguna untuk memeriksa tabel halaman dan informasi terkait dengan membaca file di /proc
.
Prosedur umum untuk menggunakan peta halaman untuk mengetahui apakah dua proses berbagi memori berjalan seperti ini:
- Baca
/proc/<pid>/maps
untuk kedua proses untuk menentukan bagian mana dari ruang memori yang dipetakan ke objek mana. - Pilih peta yang Anda minati, dalam hal ini halaman tempat
libmyl.so
dipetakan. - Buka
/proc/<pid>/pagemap
.pagemap
terdiri dari deskriptor peta halaman 64-bit, satu per halaman. Pemetaan antara alamat halaman dan alamat deskriptornya dipagemap
adalah alamat halaman / ukuran halaman * ukuran deskriptor . Carilah deskriptor halaman yang ingin Anda periksa. - Baca deskriptor 64-bit sebagai bilangan bulat yang tidak ditandatangani untuk setiap halaman dari
pagemap
. - Bandingkan nomor bingkai halaman (PFN) dalam bit 0-54 dari deskriptor halaman antara
libmyl.so
halaman untukvictim
danspy
. Jika PFN cocok, kedua proses berbagi halaman fisik yang sama.
Contoh kode berikut mengilustrasikan bagaimana pagemap
dapat diakses dan dicetak dari dalam proses. Ini menggunakan dl_iterate_phdr()
untuk menentukan alamat virtual dari setiap pustaka bersama yang dimuat ke dalam ruang memori proses, kemudian mencari dan mencetak pagemap
yang sesuai dari /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 %snn",
(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 %7sn",
"", "", "soft-", "", "file /", "", "");
printf("%10s %8s %7s %5s %11s %7s %7sn",
"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 %7dn", 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);
}
Output dari program akan terlihat seperti berikut:
$ 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
...
dimana:
address
adalah alamat virtual halamanpfn
adalah nomor bingkai halaman halamansoft-dirty
menunjukkan jika bit kotor lunak diatur di halaman Entri Tabel Halaman (PTE).excl.
menunjukkan apakah halaman dipetakan secara eksklusif (yaitu halaman hanya dipetakan untuk proses ini).file / shared anon
menunjukkan apakah halaman tersebut adalah halaman file atau halaman anonim bersama.swapped
menunjukkan jika halaman saat ini ditukar (menyiratkanpresent
adalah nol).present
menunjukkan jika halaman saat ini ada dalam proses tetap set (menyiratkanswapped
adalah nol).
(Catatan:Saya menjalankan program contoh dengan sudo
karena sejak Linux 4.0 hanya pengguna dengan CAP_SYS_ADMIN
kapabilitas bisa mendapatkan PFN dari /proc/<pid>/pagemap
. Mulai dari Linux 4.2, bidang PFN di-nolkan jika pengguna tidak memiliki CAP_SYS_ADMIN
. Alasan perubahan ini adalah untuk mempersulit eksploitasi kerentanan terkait memori lainnya, serangan Rowhammer, menggunakan informasi pada pemetaan virtual-ke-fisik yang diekspos oleh PFN.)
Jika Anda menjalankan program contoh beberapa kali, Anda akan melihat bahwa alamat virtual halaman harus berubah (karena ASLR), tetapi PFN untuk pustaka bersama yang digunakan oleh proses lain harus tetap sama.
Jika PFN untuk libmyl.so
kecocokan antara victim
dan spy
program, saya akan mulai mencari alasan mengapa serangan itu gagal dalam kode serangan itu sendiri. Jika PFN tidak cocok, bit tambahan mungkin memberikan beberapa petunjuk mengapa halaman tidak diatur untuk dibagikan. pagemap
bit menunjukkan hal berikut:
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)
Salin halaman tulis di (MAP_FILE | MAP_PRIVATE)
area anonim dalam konteks ini.
Bonus: Untuk mendapatkan berapa kali halaman telah dipetakan, PFN dapat digunakan untuk mencari halaman di /proc/kpagecount
. File ini berisi 64-bit hitungan berapa kali setiap halaman dipetakan, diindeks oleh PFN.