欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

Toyko Western 2019 gnote 解析

来源:本站整理 作者:佚名 时间:2019-09-10 TAG: 我要投稿


 
前言
Toyko Western 2019 的一道 linux kernel 驱动题,从代码上看实在找不出什么漏洞点,但是到了汇编的层面就不同了
 
前置知识
gcc 编译 switch case
gcc 编译switch... case代码的时
case 超过 5个的时候会变成jump table 的形式
我们可以写个代码测试一下,就像下面这样,写个 switch
#include
#include
#include
#include
int do_something(char *buf){
    int ret=0;
    switch(*(int *)buf){
        case 1:
            printf("case 1n");
            break;
        case 2:
            printf("case 2n");
            break;
        case 3:
            printf("case 3n");
            break;
        case 4:
            printf("case 4n");
            break;
        /*case 5:*/
            /*printf("case 5n");*/
            /*break;*/
    }
    return ret;
}
int main(int argc,char **argv){
    char *buf=malloc(0x100);
    *(int *)buf = 0x1;
    do_something(buf);
    return 0;
}
我们先看 4 个case 的情况,

在 case 不超过 5 个的情况下,默认都会编译成 cmp ... je 的形式
cmp    eax,0x3
je     690 0x30>
case 超过 5 个的情况下,会生成一个 jump table 数组,根据每个 case索引数组跳到对应的逻辑里面

660:   83 3f 05                cmp    DWORD PTR [rdi],0x5
663:   0f 87 8f 00 00 00       ja     6f8 0x98>
676:   48 63 04 82             movsxd rax,DWORD PTR [rdx+rax*4]
67a:   48 01 d0                add    rax,rdx
67d:   ff e0                   jmp    rax
从上面可以看出 rdi 就是 传入的 char *buf 的地址,首先会把他和最大的 case比较,这里是 5, 超过则跳出去,没有就根据 jump table 来执行每个 case
从汇编代码可以看到,[rdi] 做了两次的检查
一次在cmp DWORD PTR [rdi],0x5 比较最大值
一次在 mov eax,DWORD PTR [rdi] 再一次取rdi 的值作为 jump table 的索引
本来这里没有什么问题,但是如果do_something(char *buf) 传入的指针 buf的内容可以被并发修改,那么就可能会出现问题
假如 cmp 之后 buf 的内容被改变了,那么 取到 eax 里面的就可能是其他的值,也就是我们说的 double fetch 了,于是就可以构造 jump table 的数组越界,最终可以利用jmp rax jump any where
gnote 也是根据这个特点做的设计,我们接下来看看题目
 
题目分析
题目还直接给了c文件,只实现了 read/write 两个接口
write 函数
...
ssize_t gnote_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
  unsigned int index;
  mutex_lock(&lock);
  /*
   * 1. add note
   * 2. edit note
   * 3. delete note
   * 4. copy note
   * 5. select note
   * No implementation :(
   */
  switch(*(unsigned int *)buf){
    .....
  }
  mutex_unlock(&lock);
  return count;
}
write 接口实现了一个 switch...case 的逻辑,只实现了add和select的逻辑, 进入的的退出的时候会加锁
struct note {
  unsigned long size;
  char *contents;
};
unsigned long cnt;
unsigned long selected;

[1] [2] [3] [4]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载