简介

Double Fetch从漏洞原理上讲是属于条件竞争漏洞,是一种内核态与用户态之间的数据存在着访问竞争;而条件竞争漏洞我们都比较清楚,简单的来说就是多线程数据访问时,并且没有对数据做必要的安全同步措施;当多线程时,对于同一数据有一个线程在读而有另外一个线程在写,这就可能引起数据的访问异常,而此时如果这个异常访问情况发生在内核与用户线程之间时,就触发double fetch漏洞了....
为了简化漏洞,这里我们利用2018 0CTF Finals Baby Kernel来学习这个漏洞的利用方法,其中驱动的运行环境我都已经放在这个github里面了,有需要的可以下载学习....

一个典型的Double Fetch漏洞原理

一个用户态线程准备的数据通过系统调用进入内核,这个数据在内核中有两次被取用,内核第一次取用数据进行了安全检查(比如缓冲区大小、指针可用性等),当检查通过后内核第二次取用数据进行实际处理;而在两次取用数据的间隙,另一个用户态线程可以创造条件竞争,对那个已经将通过了检查的用户态数据进行篡改,使得数据在真实使用时造成访问越界或缓冲区溢出,最终导致内核崩溃或权限提升....
简单的原理示意图就是这个样子:

具体分析

现在我们直接来分析baby.ko这个驱动文件:

ida静态分析

这个驱动文件主要注册一个baby_ioctl的函数:

这个函数中主要分为2个部分,一个部分打印flag在内核中的地址:

if ( (_DWORD)a2 == 0x6666 )
  {
    printk("Your flag is at %px! But I don't think you know it's content\n", flag);
    result = 0LL;
  }

而另一部分则是直接打印出flag的值:

else if ( (_DWORD)a2 == 0x1337
         && !_chk_range_not_ok(v2, 16LL, *(_QWORD *)(current_task + 0x1358LL))
         && !_chk_range_not_ok(*(_QWORD *)v5, *(_DWORD *)(v5 + 8), *(_QWORD *)(current_task + 0x1358LL))
         && *(_DWORD *)(v5 + 8) == strlen(flag) )
  {
    for ( i = 0; i < strlen(flag); ++i )
    {
      if ( *(_BYTE *)(*(_QWORD *)v5 + i) != flag[i] )
        return 22LL;
    }
    printk("Looks like the flag is not a secret anymore. So here is it %s\n", flag);
    result = 0LL;
  }

并且我们发现flag是被硬编码在驱动文件中的:

(注意我们的目的为了不是直接得到这个flag的,而是通过Double Fetch漏洞从内核中获得她....)
但是如果想要驱动直接打印出flag的话,我们必须要绕过两处检查:
第一处是else if里面的条件:

else if ( (_DWORD)a2 == 0x1337
         && !_chk_range_not_ok(v2, 16LL, *(_QWORD *)(current_task + 0x1358LL))
         && !_chk_range_not_ok(*(_QWORD *)v5, *(_DWORD *)(v5 + 8), *(_QWORD *)(current_task + 0x1358LL))
         && *(_DWORD *)(v5 + 8) == strlen(flag) )

其中_chk_range_not_ok的内容是:

点击收藏 | 2 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖